From c2fda6be5aca460b74e2d04512f40890063f3a9d Mon Sep 17 00:00:00 2001 From: Leo Yuriev Date: Mon, 6 Mar 2017 20:18:42 +0300 Subject: [PATCH] mdbx: refine mdbx_is_dirty(). --- mdbx.c | 56 ++++++++++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/mdbx.c b/mdbx.c index 5f34b376..d201c495 100644 --- a/mdbx.c +++ b/mdbx.c @@ -11484,7 +11484,7 @@ int mdbx_is_dirty(const MDB_txn *txn, const void *ptr) { return MDBX_EBADSIGN; if (unlikely(txn->mt_flags & MDB_TXN_RDONLY)) - return MDB_BAD_TXN; + return MDBX_RESULT_FALSE; const MDB_env *env = txn->mt_env; const uintptr_t mask = ~(uintptr_t)(env->me_psize - 1); @@ -11495,49 +11495,37 @@ int mdbx_is_dirty(const MDB_txn *txn, const void *ptr) { * что было исходно задумано, детали см в логике кода mdbx_page_touch(). * * Более того, в режиме БЕЗ WRITEMAP грязные страницы выделяются через - * malloc(), т.е. находятся вне mmap-диаппазона. + * malloc(), т.е. находятся вне mmap-диапазона. * - * Тем не менее, однозначно страница "не грязная" если: - * - адрес находится внутри mmap-диаппазона и в заголовке страницы - * нет флажка P_DIRTY, то однозначно страница "не грязная". - * - адрес вне mmap-диаппазона и его нет среди списка "грязных" страниц. - */ + * Тем не менее, однозначно страница "не грязная" если адрес находится + * внутри mmap-диапазона и в заголовке страницы нет флажка P_DIRTY. */ 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; + if ((char *)page < env->me_map + used_size) { + /* страница внутри диапазона, смотрим на флажки */ + if ((page->mp_flags & (P_DIRTY | P_LOOSE | P_KEEP)) == 0) + return MDBX_RESULT_FALSE; } /* Гипотетически здесь возможна ситуация, когда указатель адресует что-то * в пределах mmap, но за границей распределенных страниц. Это тяжелая - * ошибка, которой не возможно добиться без каких-то мега-нарушений. + * ошибка, к которой не возможно прийти без каких-то больших нарушений. * Поэтому не проверяем этот случай кроме как assert-ом, ибо бестолку. */ mdbx_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; + /* Страница вне используемого mmap-диапазона, т.е. либо в функцию был + * передан некорректный адрес, либо адрес в теневой странице, которая была + * выделена посредством malloc(). + * + * Поэтому всегда считаем что страница вне mmap-диапазона "грязная", + * не просматривая при этом списки грязных и spilled страниц у каких-либо + * транзакций. Такая логика имеет ряд преимуществ: + * - не тратим время на просмотр списков; + * - результат всегда безопасен (может быть ложно-положительным, но + * не ложно-отрицательным); + * - результат не зависит от вложенности транзакций и от относительного + * положения переданной транзакции в этой рекурсии. */ + return MDBX_RESULT_TRUE; } int mdbx_dbi_open_ex(MDB_txn *txn, const char *name, unsigned flags,