From e080be163188e0c8f462c05eaea2da0d33498d8f Mon Sep 17 00:00:00 2001 From: Leo Yuriev Date: Mon, 30 Jan 2017 20:16:12 +0300 Subject: [PATCH] mdbx: add mdbx_is_dirty() for libfpta. --- mdbx.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mdbx.h | 2 ++ 2 files changed, 89 insertions(+) diff --git a/mdbx.c b/mdbx.c index bab44d87..3e8f93d4 100644 --- a/mdbx.c +++ b/mdbx.c @@ -567,3 +567,90 @@ mdbx_get_ex(MDB_txn *txn, MDB_dbi dbi, } 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; +} diff --git a/mdbx.h b/mdbx.h index 4e85f55d..f312d677 100644 --- a/mdbx.h +++ b/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. */ 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