mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-06 18:44:13 +08:00
mdbx: add mdbx_is_dirty() for libfpta.
This commit is contained in:
parent
55c43291c6
commit
e080be1631
87
mdbx.c
87
mdbx.c
@ -567,3 +567,90 @@ mdbx_get_ex(MDB_txn *txn, MDB_dbi dbi,
|
|||||||
}
|
}
|
||||||
return MDB_SUCCESS;
|
return MDB_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Функция сообщает находится ли указанный адрес в "грязной" странице у
|
||||||
|
* заданной пишущей транзакции. В конечном счете это позволяет избавиться от
|
||||||
|
* лишнего копирования данных из НЕ-грязных страниц.
|
||||||
|
*
|
||||||
|
* "Грязные" страницы - это те, которые уже были изменены в ходе пишущей
|
||||||
|
* транзакции. Соответственно, какие-либо дальнейшие изменения могут привести
|
||||||
|
* к перезаписи таких страниц. Поэтому все функции, выполняющие изменения, в
|
||||||
|
* качестве аргументов НЕ должны получать указатели на данные в таких
|
||||||
|
* страницах. В свою очередь "НЕ грязные" страницы перед модификацией будут
|
||||||
|
* скопированы.
|
||||||
|
*
|
||||||
|
* Другими словами, данные из "грязных" страниц должны быть либо скопированы
|
||||||
|
* перед передачей в качестве аргументов для дальнейших модификаций, либо
|
||||||
|
* отвергнуты на стадии проверки корректности аргументов.
|
||||||
|
*
|
||||||
|
* Таким образом, функция позволяет как избавится от лишнего копирования,
|
||||||
|
* так и выполнить более полную проверку аргументов.
|
||||||
|
*
|
||||||
|
* ВАЖНО: Передаваемый указатель должен указывать на начало данных. Только
|
||||||
|
* так гарантируется что актуальный заголовок страницы будет физически
|
||||||
|
* расположен в той-же странице памяти, в том числе для многостраничных
|
||||||
|
* P_OVERFLOW страниц с длинными данными. */
|
||||||
|
int mdbx_is_dirty(const MDB_txn *txn, const void* ptr)
|
||||||
|
{
|
||||||
|
if (unlikely(!txn))
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
if(unlikely(txn->mt_signature != MDBX_MT_SIGNATURE))
|
||||||
|
return MDB_VERSION_MISMATCH;
|
||||||
|
|
||||||
|
if (unlikely(txn->mt_flags & MDB_TXN_RDONLY))
|
||||||
|
return MDB_BAD_TXN;
|
||||||
|
|
||||||
|
const MDB_env *env = txn->mt_env;
|
||||||
|
const uintptr_t mask = ~(uintptr_t) (env->me_psize - 1);
|
||||||
|
const MDB_page *page = (const MDB_page *) ((uintptr_t) ptr & mask);
|
||||||
|
|
||||||
|
/* LY: Тут не всё хорошо с абсолютной достоверностью результата,
|
||||||
|
* так как флажок P_DIRTY в LMDB может означать не совсем то,
|
||||||
|
* что было исходно задумано, детали см в логике кода mdb_page_touch().
|
||||||
|
*
|
||||||
|
* Более того, в режиме БЕЗ WRITEMAP грязные страницы выделяются через
|
||||||
|
* malloc(), т.е. находятся вне mmap-диаппазона.
|
||||||
|
*
|
||||||
|
* Тем не менее, однозначно страница "не грязная" если:
|
||||||
|
* - адрес находится внутри mmap-диаппазона и в заголовке страницы
|
||||||
|
* нет флажка P_DIRTY, то однозначно страница "не грязная".
|
||||||
|
* - адрес вне mmap-диаппазона и его нет среди списка "грязных" страниц.
|
||||||
|
*/
|
||||||
|
if (env->me_map < (char*) page) {
|
||||||
|
const size_t used_size = env->me_psize * txn->mt_next_pgno;
|
||||||
|
if (env->me_map + used_size > (char*) page) {
|
||||||
|
/* страница внутри диапазона */
|
||||||
|
if (page->mp_flags & P_DIRTY)
|
||||||
|
return MDBX_RESULT_TRUE;
|
||||||
|
return MDBX_RESULT_FALSE;
|
||||||
|
}
|
||||||
|
/* Гипотетически здесь возможна ситуация, когда указатель адресует что-то
|
||||||
|
* в пределах mmap, но за границей распределенных страниц. Это тяжелая
|
||||||
|
* ошибка, которой не возможно добиться без каких-то мега-нарушений.
|
||||||
|
* Поэтому не проверяем этот случай кроме как assert-ом, ибо бестолку. */
|
||||||
|
mdb_tassert(txn, env->me_map + env->me_mapsize > (char*) page);
|
||||||
|
}
|
||||||
|
/* Страница вне mmap-диаппазона */
|
||||||
|
|
||||||
|
if (env->me_flags & MDB_WRITEMAP)
|
||||||
|
/* Если MDB_WRITEMAP, то результат уже ясен. */
|
||||||
|
return MDBX_RESULT_FALSE;
|
||||||
|
|
||||||
|
/* Смотрим список грязных страниц у заданной транзакции. */
|
||||||
|
MDB_ID2 *list = txn->mt_u.dirty_list;
|
||||||
|
if (list) {
|
||||||
|
unsigned i, n = list[0].mid;
|
||||||
|
for (i = 1; i <= n; i++) {
|
||||||
|
const MDB_page *dirty = list[i].mptr;
|
||||||
|
if (dirty == page)
|
||||||
|
return MDBX_RESULT_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* При вложенных транзакциях, страница может быть в dirty-списке
|
||||||
|
* родительской транзакции, но в этом случае она будет скопирована перед
|
||||||
|
* изменением в текущей транзакции, т.е. относительно заданной транзакции
|
||||||
|
* проверяемый адрес "не грязный". */
|
||||||
|
return MDBX_RESULT_FALSE;
|
||||||
|
}
|
||||||
|
2
mdbx.h
2
mdbx.h
@ -235,6 +235,8 @@ int mdbx_replace(MDB_txn *txn, MDB_dbi dbi,
|
|||||||
* 2) updates the key for pointing to the actual key's data inside DB. */
|
* 2) updates the key for pointing to the actual key's data inside DB. */
|
||||||
int mdbx_get_ex(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, int* values_count);
|
int mdbx_get_ex(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, int* values_count);
|
||||||
|
|
||||||
|
int mdbx_is_dirty(const MDB_txn *txn, const void* ptr);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
Loading…
x
Reference in New Issue
Block a user