From 009618560f1ebc8842b2383a4b60755f0810dd08 Mon Sep 17 00:00:00 2001
From: Leo Yuriev <leo@yuriev.ru>
Date: Tue, 30 May 2017 15:19:46 +0300
Subject: [PATCH] mdbx: refine mdbx_is_dirty().

---
 src/mdbx.c | 37 ++++++++++++++++++++++---------------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/src/mdbx.c b/src/mdbx.c
index 16294080..6e204553 100644
--- a/src/mdbx.c
+++ b/src/mdbx.c
@@ -10257,38 +10257,45 @@ int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr) {
    * что было исходно задумано, детали см в логике кода mdbx_page_touch().
    *
    * Более того, в режиме БЕЗ WRITEMAP грязные страницы выделяются через
-   * malloc(), т.е. находятся вне mmap-диапазона.
+   * malloc(), т.е. находятся вне mmap-диапазона и тогда чтобы отличить
+   * действительно грязную страницу от указателя на данные пользователя
+   * следует сканировать dirtylist, что накладно.
    *
-   * Тем не менее, однозначно страница "не грязная" если адрес находится
-   * внутри mmap-диапазона и в заголовке страницы нет флажка P_DIRTY. */
+   * Тем не менее, однозначно страница "не грязная" (не будет переписана
+   * во время транзакции) если адрес находится внутри mmap-диапазона
+   * и в заголовке страницы нет флажка P_DIRTY. */
   if (env->me_map < (char *)page) {
     const size_t used_size = env->me_psize * txn->mt_next_pgno;
     if ((char *)page < env->me_map + used_size) {
-      /* страница внутри диапазона, смотрим на флажки  */
-      if ((page->mp_flags & (P_DIRTY | P_LOOSE | P_KEEP)) == 0)
-        return MDBX_RESULT_FALSE;
+      /* страница внутри диапазона, смотрим на флажки */
+      return (page->mp_flags & (P_DIRTY | P_LOOSE | P_KEEP))
+                 ? MDBX_RESULT_TRUE
+                 : MDBX_RESULT_FALSE;
     }
     /* Гипотетически здесь возможна ситуация, когда указатель адресует что-то
      * в пределах mmap, но за границей распределенных страниц. Это тяжелая
      * ошибка, к которой не возможно прийти без каких-то больших нарушений.
-     * Поэтому не проверяем этот случай кроме как assert-ом, ибо бестолку. */
-    /* if (!RUNNING_ON_VALGRIND)
-      mdbx_tassert(txn, env->me_map + env->me_mapsize >= (char *)page); */
+     * Поэтому не проверяем этот случай кроме как assert-ом, на то что
+     * страница вне mmap-диаппазона. */
+    mdbx_tassert(txn, (char *)page >= env->me_map + env->me_mapsize);
   }
 
   /* Страница вне используемого mmap-диапазона, т.е. либо в функцию был
    * передан некорректный адрес, либо адрес в теневой странице, которая была
    * выделена посредством malloc().
    *
-   * Поэтому всегда считаем что страница вне mmap-диапазона "грязная",
-   * не просматривая при этом списки грязных и spilled страниц у каких-либо
-   * транзакций. Такая логика имеет ряд преимуществ:
+   * Для WRITE_MAP режима такая страница однозначно "не грязная",
+   * а для режимов без WRITE_MAP следует просматривать списки dirty
+   * и spilled страниц у каких-либо транзакций (в том числе дочерних).
+   *
+   * Поэтому для WRITE_MAP возвращаем false, а для остальных режимов
+   * всегда true. Такая логика имеет ряд преимуществ:
    *  - не тратим время на просмотр списков;
-   *  - результат всегда безопасен (может быть ложно-положительным, но
-   *    не ложно-отрицательным);
+   *  - результат всегда безопасен (может быть ложно-положительным,
+   *    но не ложно-отрицательным);
    *  - результат не зависит от вложенности транзакций и от относительного
    *    положения переданной транзакции в этой рекурсии. */
-  return MDBX_RESULT_TRUE;
+  return (env->me_flags & MDBX_WRITEMAP) ? MDBX_RESULT_FALSE : MDBX_RESULT_TRUE;
 }
 
 int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result,