private_bcb_flush_tree->private_bcb_lsn_tree: non-LSN dirty blocks not included
authorshort <>
Sun, 13 Jul 2003 15:51:36 +0000 (15:51 +0000)
committershort <>
Sun, 13 Jul 2003 15:51:36 +0000 (15:51 +0000)
+PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR: Proper derefercing while privbcb destroy.
+many debug dumps
private_bcb_hash_value_destroy_func(): +sanity check for !privbcb->valid_lsn
CcUnpinData_leave_func(): Cleanup and flush only minimal set of privbcb-s

src/libcaptive/cc/map.c

index 6d3f47c..e06c9bc 100644 (file)
@@ -321,62 +321,74 @@ struct private_bcb {
 
 
 /* map: (struct private_bcb *)privbcb -> (struct private_bcb *)privbcb */
-static GTree *private_bcb_flush_tree;
+static GTree *private_bcb_lsn_tree;
 
-static gint private_bcb_flush_tree_key_compare_func(struct private_bcb *a,struct private_bcb *b,gpointer user_data /* NULL */)
+/* Although 'private_bcb_lsn_tree' contains only nodes with 'lsn_valid' TRUE
+ * compare_func cannot expect such arguments as it may be used to lookup
+ * privbcb unlisted in 'private_bcb_lsn_tree'.
+ */
+static gint private_bcb_lsn_tree_key_compare_func(struct private_bcb *a,struct private_bcb *b,gpointer user_data /* NULL */)
 {
 gint64 a_lsn,b_lsn;
 gint r;
 
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a=%p,privbcb b=%p",G_STRLOC,a,b);
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb=%p",G_STRLOC,a->PublicBcb);
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb=%p",G_STRLOC,b->PublicBcb);
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)a->PublicBcb->MappedLength);
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)b->PublicBcb->MappedLength);
+
        g_return_val_if_fail(a!=NULL,0);
        g_return_val_if_fail(validate_Bcb(a->PublicBcb),0);
-       g_assert(!a->lsn_valid || a->lsn.QuadPart!=(LONGLONG)G_MININT64);       /* Forbid defined LSN as G_MININT64 */
+       g_assert(!a->lsn_valid || a->lsn.QuadPart!=(LONGLONG)G_MAXINT64);       /* Forbid defined LSN as G_MAXINT64 */
        g_return_val_if_fail(b!=NULL,0);
        g_return_val_if_fail(validate_Bcb(b->PublicBcb),0);
-       g_assert(!b->lsn_valid || b->lsn.QuadPart!=(LONGLONG)G_MININT64);       /* Forbid defined LSN as G_MININT64 */
+       g_assert(!b->lsn_valid || b->lsn.QuadPart!=(LONGLONG)G_MAXINT64);       /* Forbid defined LSN as G_MAXINT64 */
 
-       if (a==b)       /* LSN would be apparently the same in such case :-) */
+       if (a==b) {     /* LSN would be apparently the same in such case :-) */
+               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return 0 (a==b)",G_STRLOC);
                return 0;
+               }
 
-       a_lsn=(!a->lsn_valid ? (LONGLONG)G_MININT64 : a->lsn.QuadPart);
-       b_lsn=(!b->lsn_valid ? (LONGLONG)G_MININT64 : b->lsn.QuadPart);
+       a_lsn=(!a->lsn_valid ? (LONGLONG)G_MAXINT64 : a->lsn.QuadPart);
+       b_lsn=(!b->lsn_valid ? (LONGLONG)G_MAXINT64 : b->lsn.QuadPart);
 
        /* Forbid the same LSNs if both defined */
-       g_assert(a_lsn==(LONGLONG)G_MININT64 || a_lsn!=b_lsn);
+       g_assert(a_lsn==(LONGLONG)G_MAXINT64 || a_lsn!=b_lsn);
 
-       if ((r=(a_lsn>b_lsn)-(a_lsn<b_lsn)))
+       if ((r=(a_lsn>b_lsn)-(a_lsn<b_lsn))) {
+               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %d (lsn !=)",G_STRLOC,r);
                return r;
+               }
 
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return (ptr !=)",G_STRLOC);
        return (a>b)-(a<b);
 }
 
-static void private_bcb_flush_tree_init(void)
+static void private_bcb_lsn_tree_init(void)
 {
-       if (private_bcb_flush_tree)
+       if (private_bcb_lsn_tree)
                return;
-       private_bcb_flush_tree=g_tree_new(
-                       (GCompareFunc)private_bcb_flush_tree_key_compare_func); /* key_compare_func */
+       private_bcb_lsn_tree=g_tree_new(
+                       (GCompareFunc)private_bcb_lsn_tree_key_compare_func);   /* key_compare_func */
 }
 
-#define PRIVBCB_WANTS_FLUSH(privbcb) ((privbcb)->lsn_valid || ((privbcb)->dirty && (0 \
-               || ((privbcb)->ref_count==0) \
-               || ((privbcb)->ref_count==1 && (privbcb)->leave_func_pending))))
-
 enum privbcb_item {
        PRIVBCB_ITEM_NOP,       /* for sanity checks */
        PRIVBCB_ITEM_DIRTY,
        PRIVBCB_ITEM_LSN_VALID,
        PRIVBCB_ITEM_LEAVE_FUNC_PENDING,
        PRIVBCB_ITEM_REF_COUNT,
+       PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,      /* no protections against 'leave_func_pending' */
        };
 
 static void privbcb_set(struct private_bcb *privbcb,enum privbcb_item item,gint value)
 {
        g_return_if_fail(privbcb!=NULL);
 
-       private_bcb_flush_tree_init();
+       private_bcb_lsn_tree_init();
 
-       g_assert((PRIVBCB_WANTS_FLUSH(privbcb) ? privbcb : NULL)==g_tree_lookup(private_bcb_flush_tree,privbcb));
+       g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
        switch (item) {
                case PRIVBCB_ITEM_NOP:
                        break;
@@ -395,25 +407,26 @@ static void privbcb_set(struct private_bcb *privbcb,enum privbcb_item item,gint
                                g_assert(privbcb->ref_count==1);
                        break;
                case PRIVBCB_ITEM_REF_COUNT:
-                       /* Forbid reincarnation of 'leave_func_pending' privbcb.
-                        * Allow some of its 'ref_count' handling to properly clean it up.
-                        */
-                       if (privbcb->leave_func_pending)
-                               g_assert((privbcb->ref_count==1 && value==-1)
-                                     || (privbcb->ref_count==0 && value==+1));
+                       /* Forbid reincarnation of 'leave_func_pending' privbcb. */
+                       g_assert(!privbcb->leave_func_pending);
+                       g_assert(privbcb->ref_count>=1);
+                       privbcb->ref_count+=value;
+                       g_assert(privbcb->ref_count>=1);
+                       break;
+               case PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR:
                        g_assert(privbcb->ref_count>=0);
                        privbcb->ref_count+=value;
                        g_assert(privbcb->ref_count>=0);
                        break;
                default: g_assert_not_reached();
                }
-       if (!PRIVBCB_WANTS_FLUSH(privbcb))
-               g_tree_remove(private_bcb_flush_tree,privbcb);
+       if (!privbcb->lsn_valid)
+               g_tree_remove(private_bcb_lsn_tree,privbcb);
        else
-               g_tree_insert(private_bcb_flush_tree,
+               g_tree_insert(private_bcb_lsn_tree,
                                privbcb,        /* key */
                                privbcb);       /* value */
-       g_assert((PRIVBCB_WANTS_FLUSH(privbcb) ? privbcb : NULL)==g_tree_lookup(private_bcb_flush_tree,privbcb));
+       g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
 }
 
 
@@ -424,6 +437,8 @@ static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
 {
        g_return_if_fail(validate_Bcb(key));
 
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: PublicBcb=%p",G_STRLOC,key);
+
        g_free(key);
 }
 
@@ -435,6 +450,8 @@ int errint;
 size_t offset;
 gpointer base_aligned;
 
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: privbcb=%p (->PublicBcb=%p)",G_STRLOC,value,value->PublicBcb);
+
        g_return_if_fail(value!=NULL);
        /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
         * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
@@ -446,6 +463,8 @@ gpointer base_aligned;
        g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
        g_return_if_fail(value->base!=NULL);
        g_return_if_fail(value->dirty==FALSE);
+       /* Ensure we are not registered in 'private_bcb_lsn_tree'. */
+       g_return_if_fail(value->lsn_valid==FALSE);
 
        page_position_hash_init();
 
@@ -542,25 +561,42 @@ static gboolean captive_cc_FileObject_delete_private_bcb_hash_foreach(
                struct private_bcb *privbcb,  /* value */
                FILE_OBJECT *FileObject)  /* user_data */
 {
+       g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);    /* meaning: do not remove this 'privbcb' */
+       g_return_val_if_fail(privbcb!=NULL,FALSE);      /* meaning: do not remove this 'privbcb' */
+       g_return_val_if_fail(PublicBcb==privbcb->PublicBcb,FALSE);      /* meaning: do not remove this 'privbcb' */
+       g_return_val_if_fail(FileObject!=NULL,FALSE);   /* meaning: do not remove this 'privbcb' */
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: PublicBcb=%p,privbcb=%p,privbcb->ref_count=%d,privbcb->leave_func_pending=%d"
+                       ",FileObject=%p",G_STRLOC,
+                       PublicBcb,privbcb,privbcb->ref_count,privbcb->leave_func_pending,FileObject);
+
        if (FileObject) {
                if (privbcb->FileObject!=FileObject)
                        return FALSE;   /* do not remove this 'privbcb' */
 
-               /* FIXME: Such 'privbcb' can get possibly reincarnated!
-                * We disallow even already-deleted entries during !FileObject (unmount).
+               /* It is OK to leave it in 'private_bcb_hash' as it is protected
+                * against reincarnation.
+                * Such check is here to allow it to keep 'dirty' data.
                 */
-               if (privbcb->ref_count==1 && privbcb->leave_func_pending)
+               if (privbcb->leave_func_pending)
                        return FALSE;   /* do not remove this 'privbcb' */
                }
 
+       /* Do not reuse CcUnpinData_leave_func() destructor here as we cannot
+        * call g_hash_table_remove() from inside g_hash_table_foreach_remove() callback.
+        */
+
        g_assert(privbcb->dirty==FALSE);        /* it would be fatal */
 
-       /* Force 'ref_count' drop to 0. */
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-privbcb->ref_count);
+       g_assert(privbcb->leave_func_pending==FALSE);
+       /* Force 'ref_count' drop to 0 to pass private_bcb_hash_value_destroy_func(). */
+       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-privbcb->ref_count);
+       /* Unset LSN to unlink from 'private_bcb_lsn_tree'. */
+       privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
 
        g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: WARNING: Deleting file with pending (fortunately non-dirty) BCBs"
-                       "; FileObject=%p,FileOffset=0x%llX,Length=0x%lX",G_STRLOC,
-                       privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
+                       "; privbcb=%p,PublicBcb=%p,FileObject=%p,FileOffset=0x%llX,Length=0x%lX",G_STRLOC,
+                       privbcb,PublicBcb,privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
 
        return TRUE;    /* remove this 'privbcb' */
 }
@@ -572,6 +608,8 @@ BOOLEAN r=TRUE;
 
        /* 'FileObject' may be NULL to check/clear the whole cache. */
 
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p",G_STRLOC,FileObject);
+
        fileobject_cached_hash_init();
        private_bcb_hash_init();
 
@@ -589,6 +627,8 @@ BOOLEAN r=TRUE;
                        (GHRFunc)captive_cc_FileObject_delete_private_bcb_hash_foreach, /* func */
                        FileObject);    /* user_data */
 
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: returning r=%d",G_STRLOC,r);
+
        return r;
 }
 
@@ -611,7 +651,7 @@ static void captive_cc_flush_private_bcb_hash_foreach(
        captive_privbcb_flush(privbcb);
 }
 
-static void CcUnpinData_leave_func(struct private_bcb *privbcb_stop_at);
+static void CcUnpinData_leave_func(struct private_bcb *privbcb_req);
 
 void captive_cc_flush(void)
 {
@@ -853,6 +893,8 @@ GPtrArray *read_array;
        privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
        privbcb->dirty=FALSE;
        privbcb->lsn_valid=FALSE;
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_hash_table_insert: PublicBcb=%p,privbcb=%p",G_STRLOC,
+                       PublicBcb,privbcb);
        g_hash_table_insert(private_bcb_hash,
                        PublicBcb,      /* key */
                        privbcb);       /* value */
@@ -1201,10 +1243,10 @@ static gint64 last_written_lsn=G_MININT64;
        if (!privbcb->dirty)
                return;
 
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
+       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1);
        logging_notify_privbcb_flush(privbcb);
        g_assert(privbcb->ref_count>=2);
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
+       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
 
        if (privbcb->lsn_valid) {
                if (!(last_written_lsn<privbcb->lsn.QuadPart))
@@ -1304,36 +1346,85 @@ REACTOS_COMMON_FCB_HEADER *CommonFcb=(REACTOS_COMMON_FCB_HEADER *)privbcb->FileO
 }
 
 
+struct CcUnpinData_leave_func_foreach_get_first_args {
+       struct private_bcb *privbcb_req;
+       struct private_bcb *found;      /* return */
+       };
+
 static gboolean CcUnpinData_leave_func_foreach_get_first(struct private_bcb *key,struct private_bcb *value,
-               struct private_bcb **privbcbp /* data */)
+               struct CcUnpinData_leave_func_foreach_get_first_args *args /* data */)
 {
        g_return_val_if_fail(key!=NULL,TRUE);   /* meaning: stop the traversal */
        g_return_val_if_fail(value!=NULL,TRUE); /* meaning: stop the traversal */
        g_return_val_if_fail(key==value,TRUE);  /* meaning: stop the traversal */
        g_return_val_if_fail(validate_Bcb(key->PublicBcb),TRUE);        /* meaning: stop the traversal */
-       g_return_val_if_fail(privbcbp!=NULL,TRUE);      /* meaning: stop the traversal */
-       g_return_val_if_fail(*privbcbp==NULL,TRUE);     /* meaning: stop the traversal */
+       g_return_val_if_fail(args!=NULL,TRUE);  /* meaning: stop the traversal */
+       g_return_val_if_fail(args->found==NULL,TRUE);   /* meaning: stop the traversal */
 
-       *privbcbp=key;
+       g_assert(key->lsn_valid==TRUE);
 
+       if (key==args->privbcb_req) {
+               args->found=key;
+               return TRUE;    /* stop the traversal */
+               }
+
+       if (!key->dirty)
+               return FALSE;   /* continue the traversal */
+
+       args->found=key;        /* first dirty LSNed buffer */
        return TRUE;    /* stop the traversal */
 }
 
-static void CcUnpinData_leave_func(struct private_bcb *privbcb_stop_at)        /* NULL to traverse the whole tree */
+static void CcUnpinData_leave_func_finalize(struct private_bcb *privbcb)
 {
-struct private_bcb *privbcb;
 gboolean errbool;
 
-       private_bcb_flush_tree_init();
+       g_assert(privbcb->dirty==FALSE);
+       g_assert(privbcb->lsn_valid==FALSE);
+       g_assert(NULL==g_tree_lookup(private_bcb_lsn_tree,privbcb));
+       g_assert(privbcb->leave_func_pending==TRUE);
+       g_assert(privbcb->ref_count==1);
+       privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,FALSE);
+       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
+       errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
+       g_assert(errbool==TRUE);
+}
 
-       do {
-               privbcb=NULL;
-               g_tree_foreach(private_bcb_flush_tree,
+static void CcUnpinData_leave_func(struct private_bcb *privbcb_req)    /* NULL to traverse the whole tree */
+{
+struct private_bcb *privbcb;
+struct CcUnpinData_leave_func_foreach_get_first_args args;
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb_req=%p,privbcb_req->ref_count=%d,privbcb_req->leave_func_pending=%d"
+                       ",privbcb_req->lsn_valid=%d",G_STRLOC,
+                       privbcb_req,(!privbcb_req ? -1 : privbcb_req->ref_count),(!privbcb_req ? -1 : privbcb_req->leave_func_pending),
+                       (!privbcb_req ? -1 : privbcb_req->lsn_valid));
+
+       if ((privbcb=privbcb_req) && !privbcb_req->lsn_valid) {
+               g_assert(NULL==g_tree_lookup(private_bcb_lsn_tree,privbcb));
+               g_assert(privbcb->leave_func_pending==TRUE);
+               g_assert(privbcb->ref_count==1);
+               if (privbcb->dirty)
+                       captive_privbcb_flush(privbcb);
+               CcUnpinData_leave_func_finalize(privbcb);
+               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return",G_STRLOC);
+               return;
+               }
+       g_assert(!privbcb_req || privbcb_req==g_tree_lookup(private_bcb_lsn_tree,privbcb_req));
+
+       private_bcb_lsn_tree_init();
+
+       for (;;) {
+               args.privbcb_req=privbcb_req;
+               args.found=NULL;
+               g_tree_foreach(private_bcb_lsn_tree,
                                (GTraverseFunc)CcUnpinData_leave_func_foreach_get_first,        /* func */
-                               &privbcb);      /* user_data */
-               g_assert((!privbcb)==!g_tree_nnodes(private_bcb_flush_tree));
-               if (!privbcb)
+                               &args); /* user_data */
+               if (args.found==NULL) {
+                       g_assert(privbcb_req==NULL);    /* Global LSN-ordered flush has no more entries. */
                        break;
+                       }
+               privbcb=args.found;
 
                g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
                                "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
@@ -1341,48 +1432,23 @@ gboolean errbool;
                                privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
                                (int)privbcb->leave_func_pending);
 
-               /* Do not stop at not yet modified buffers if not privbcb_stop_at==NULL,
-                * privbcb_stop_at==NULL is used to flush the whole cache.
-                */
-               if (privbcb_stop_at && !privbcb->leave_func_pending) {
-                       g_assert(privbcb->lsn_valid);   /* cannout skip LSN-valid BCB */
-                       break;
-                       }
-
-               g_assert(!privbcb_stop_at || privbcb->leave_func_pending==TRUE);
-               privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,FALSE);
-               privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
-
-               /* Do not flush modified buffers if not privbcb_stop_at==NULL,
-                * privbcb_stop_at==NULL is used to flush the whole cache.
-                */
-               if (privbcb_stop_at && privbcb->ref_count) {
-                       if (privbcb->lsn_valid) /* cannout skip LSN-valid BCB */
-                               break;
-                       continue;
-                       }
-
-               /* We were called as CcUnpinData()->captive_privbcb_flush()->CcUnpinData()->CcUnpinData_leave_func()
-                */
                if (privbcb->dirty) {
-                       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
                        captive_privbcb_flush(privbcb);
-                       continue;
+                       continue;       /* Restart as anything could change by calling W32 hassle. */
                        }
 
-               /* Clear it out of 'private_bcb_flush_tree' during global buffers flush. */
-               if (!privbcb_stop_at) {
-                       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-privbcb->ref_count);
-                       privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
-                       }
+               if (privbcb!=privbcb_req)
+                       continue;       /* We just flushed a LSNed buffer not yet requested to be destroyed. */
 
-               g_assert(privbcb->leave_func_pending==FALSE);
-               g_assert(privbcb->ref_count==0);
-               g_assert(NULL==g_tree_lookup(private_bcb_flush_tree,privbcb));
+               g_assert(privbcb==g_tree_lookup(private_bcb_lsn_tree,privbcb));
+               g_assert(privbcb->leave_func_pending==TRUE);
+               g_assert(privbcb->ref_count==1);
+               privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
+               CcUnpinData_leave_func_finalize(privbcb);
+               break;
+               }
 
-               errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
-               g_assert(errbool==TRUE);
-               } while (!privbcb_stop_at || privbcb!=privbcb_stop_at);
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return",G_STRLOC);
 }
 
 /**
@@ -1404,6 +1470,7 @@ struct private_bcb *privbcb;
        PublicBcb=(PUBLIC_BCB *)Bcb;
        privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
        g_return_if_fail(privbcb!=NULL);
+       g_assert(!privbcb->leave_func_pending);
 
        g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
        /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
@@ -1414,11 +1481,9 @@ struct private_bcb *privbcb;
                        privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
                        (int)privbcb->leave_func_pending);
 
-       g_assert(privbcb->ref_count>0);
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
+       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
        if (privbcb->ref_count>0)
                return;
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1); /* it will get decreased again in CcUnpinData_leave_func() */
 
        g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
        /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
@@ -1431,10 +1496,11 @@ struct private_bcb *privbcb;
         * I expect it a bug in ntfs.sys.
         */
        if (!privbcb->leave_func_pending) {
+               privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1);      /* '+1' gets decreased by 'leave_func_pending'. */
                privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,TRUE);
                captive_leave_register(
                                (captive_leave_func)CcUnpinData_leave_func,     /* func */
-                               privbcb);       /* data; privbcb_stop_at */
+                               privbcb);       /* data; privbcb_req */
                }
 
        if (captive_cc_unmounting)