Make tdb transaction lock recursive (samba version)
authorRusty Russell <rusty@rustcorp.com.au>
Sat, 18 Jul 2009 05:58:58 +0000 (15:28 +0930)
committerMichael Adam <obnox@samba.org>
Mon, 20 Jul 2009 20:17:20 +0000 (22:17 +0200)
This patch replaces 6ed27edbcd3ba1893636a8072c8d7a621437daf7 and
1a416ff13ca7786f2e8d24c66addf00883e9cb12, which fixed the bug where traversals
inside transactions would release the transaction lock early.

This solution is more general, and solves the more minor symptom that nested
traversals would also release the transaction lock early.  (It was also suggestd in
Volker's comment in 6ed27ed).

This patch also applies to ctdb, if the traverse.c part is removed (ctdb's tdb
code never received the previous two fixes).

Tested using the testsuite from ccan (adapted to the samba code).  Thanks to
Michael Adam for feedback.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Michael Adam <obnox@samba.org>
lib/tdb/common/lock.c
lib/tdb/common/tdb_private.h
lib/tdb/common/traverse.c

index f156c0fa7b2e548640d47db23df71c9427ec73ce..d812fbfdc5e6e3e03775314fe01e517627a78830 100644 (file)
@@ -301,16 +301,21 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
  */
 int tdb_transaction_lock(struct tdb_context *tdb, int ltype)
 {
-       if (tdb->have_transaction_lock || tdb->global_lock.count) {
+       if (tdb->global_lock.count) {
+               return 0;
+       }
+       if (tdb->transaction_lock_count > 0) {
+               tdb->transaction_lock_count++;
                return 0;
        }
+
        if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, ltype, 
                                     F_SETLKW, 0, 1) == -1) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_lock: failed to get transaction lock\n"));
                tdb->ecode = TDB_ERR_LOCK;
                return -1;
        }
-       tdb->have_transaction_lock = 1;
+       tdb->transaction_lock_count++;
        return 0;
 }
 
@@ -320,12 +325,16 @@ int tdb_transaction_lock(struct tdb_context *tdb, int ltype)
 int tdb_transaction_unlock(struct tdb_context *tdb)
 {
        int ret;
-       if (!tdb->have_transaction_lock) {
+       if (tdb->global_lock.count) {
+               return 0;
+       }
+       if (tdb->transaction_lock_count > 0) {
+               tdb->transaction_lock_count--;
                return 0;
        }
        ret = tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1);
        if (ret == 0) {
-               tdb->have_transaction_lock = 0;
+               tdb->transaction_lock_count = 0;
        }
        return ret;
 }
index ffac89ff0e315195b3defdebc35184ef7ab6a038..45b85f4c9334b91084e80141937ea029f443c037 100644 (file)
@@ -166,7 +166,7 @@ struct tdb_context {
        struct tdb_transaction *transaction;
        int page_size;
        int max_dead_records;
-       bool have_transaction_lock;
+       int transaction_lock_count;
        volatile sig_atomic_t *interrupt_sig_ptr;
 };
 
index 69c81e6e98fd27b7a4d8d86c7c6f620e64cd552a..07b0c238587eb85fe3945b61b71dbd9dd0729d58 100644 (file)
@@ -204,23 +204,18 @@ int tdb_traverse_read(struct tdb_context *tdb,
 {
        struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK };
        int ret;
-       bool in_transaction = (tdb->transaction != NULL);
 
        /* we need to get a read lock on the transaction lock here to
           cope with the lock ordering semantics of solaris10 */
-       if (!in_transaction) {
-               if (tdb_transaction_lock(tdb, F_RDLCK)) {
-                       return -1;
-               }
+       if (tdb_transaction_lock(tdb, F_RDLCK)) {
+               return -1;
        }
 
        tdb->traverse_read++;
        ret = tdb_traverse_internal(tdb, fn, private_data, &tl);
        tdb->traverse_read--;
 
-       if (!in_transaction) {
-               tdb_transaction_unlock(tdb);
-       }
+       tdb_transaction_unlock(tdb);
 
        return ret;
 }
@@ -237,25 +232,20 @@ int tdb_traverse(struct tdb_context *tdb,
 {
        struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK };
        int ret;
-       bool in_transaction = (tdb->transaction != NULL);
 
        if (tdb->read_only || tdb->traverse_read) {
                return tdb_traverse_read(tdb, fn, private_data);
        }
        
-       if (!in_transaction) {
-               if (tdb_transaction_lock(tdb, F_WRLCK)) {
-                       return -1;
-               }
+       if (tdb_transaction_lock(tdb, F_WRLCK)) {
+               return -1;
        }
 
        tdb->traverse_write++;
        ret = tdb_traverse_internal(tdb, fn, private_data, &tl);
        tdb->traverse_write--;
 
-       if (!in_transaction) {
-               tdb_transaction_unlock(tdb);
-       }
+       tdb_transaction_unlock(tdb);
 
        return ret;
 }