Keep the tdb code in sync with Samba4.
[samba.git] / source / lib / tdb / common / lock.c
index 43be5d20e1eb9a92cc5dd3d0df14437927e47800..e3fe888c465f357adb436b7230d8e3070f073a63 100644 (file)
@@ -27,6 +27,8 @@
 
 #include "tdb_private.h"
 
+#define TDB_MARK_LOCK 0x80000000
+
 void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
 {
        tdb->interrupt_sig_ptr = ptr;
@@ -116,10 +118,13 @@ int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len)
 
 
 /* lock a list in the database. list -1 is the alloc list */
-int tdb_lock(struct tdb_context *tdb, int list, int ltype)
+static int _tdb_lock(struct tdb_context *tdb, int list, int ltype, int op)
 {
        struct tdb_lock_type *new_lck;
        int i;
+       bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
+
+       ltype &= ~TDB_MARK_LOCK;
 
        /* a global lock allows us to avoid per chain locks */
        if (tdb->global_lock.count && 
@@ -169,10 +174,9 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype)
 
        /* Since fcntl locks don't nest, we do a lock for the first one,
           and simply bump the count for future ones */
-       if (tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW,
+       if (!mark_lock &&
+           tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list, ltype, op,
                                     0, 1)) {
-               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d "
-                        "ltype=%d (%s)\n",  list, ltype, strerror(errno)));
                return -1;
        }
 
@@ -186,6 +190,25 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype)
        return 0;
 }
 
+/* lock a list in the database. list -1 is the alloc list */
+int tdb_lock(struct tdb_context *tdb, int list, int ltype)
+{
+       int ret;
+       ret = _tdb_lock(tdb, list, ltype, F_SETLKW);
+       if (ret) {
+               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d "
+                        "ltype=%d (%s)\n",  list, ltype, strerror(errno)));
+       }
+       return ret;
+}
+
+/* lock a list in the database. list -1 is the alloc list. non-blocking lock */
+int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype)
+{
+       return _tdb_lock(tdb, list, ltype, F_SETLK);
+}
+
+
 /* unlock the database: returns void because it's too late for errors. */
        /* changed to return int it may be interesting to know there
           has been an error  --simo */
@@ -194,6 +217,9 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
        int ret = -1;
        int i;
        struct tdb_lock_type *lck = NULL;
+       bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
+
+       ltype &= ~TDB_MARK_LOCK;
 
        /* a global lock allows us to avoid per chain locks */
        if (tdb->global_lock.count && 
@@ -238,8 +264,12 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
         * anyway.
         */
 
-       ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK,
-                                      F_SETLKW, 0, 1);
+       if (mark_lock) {
+               ret = 0;
+       } else {
+               ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK,
+                                              F_SETLKW, 0, 1);
+       }
        tdb->num_locks--;
 
        /*
@@ -266,11 +296,50 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
        return ret;
 }
 
+/*
+  get the transaction lock
+ */
+int tdb_transaction_lock(struct tdb_context *tdb, int ltype)
+{
+       if (tdb->have_transaction_lock || tdb->global_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;
+       return 0;
+}
+
+/*
+  release the transaction lock
+ */
+int tdb_transaction_unlock(struct tdb_context *tdb)
+{
+       int ret;
+       if (!tdb->have_transaction_lock) {
+               return 0;
+       }
+       ret = tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1);
+       if (ret == 0) {
+               tdb->have_transaction_lock = 0;
+       }
+       return ret;
+}
+
+
 
 
 /* lock/unlock entire database */
-static int _tdb_lockall(struct tdb_context *tdb, int ltype)
+static int _tdb_lockall(struct tdb_context *tdb, int ltype, int op)
 {
+       bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
+
+       ltype &= ~TDB_MARK_LOCK;
+
        /* There are no locks on read-only dbs */
        if (tdb->read_only || tdb->traverse_read)
                return TDB_ERRCODE(TDB_ERR_LOCK, -1);
@@ -290,9 +359,12 @@ static int _tdb_lockall(struct tdb_context *tdb, int ltype)
                return TDB_ERRCODE(TDB_ERR_LOCK, -1);
        }
 
-       if (tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, F_SETLKW, 
+       if (!mark_lock &&
+           tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, op,
                                     0, 4*tdb->header.hash_size)) {
-               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno)));
+               if (op == F_SETLKW) {
+                       TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno)));
+               }
                return -1;
        }
 
@@ -302,9 +374,15 @@ static int _tdb_lockall(struct tdb_context *tdb, int ltype)
        return 0;
 }
 
+
+
 /* unlock entire db */
 static int _tdb_unlockall(struct tdb_context *tdb, int ltype)
 {
+       bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
+
+       ltype &= ~TDB_MARK_LOCK;
+
        /* There are no locks on read-only dbs */
        if (tdb->read_only || tdb->traverse_read) {
                return TDB_ERRCODE(TDB_ERR_LOCK, -1);
@@ -319,7 +397,8 @@ static int _tdb_unlockall(struct tdb_context *tdb, int ltype)
                return 0;
        }
 
-       if (tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 
+       if (!mark_lock &&
+           tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 
                                     0, 4*tdb->header.hash_size)) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno)));
                return -1;
@@ -334,7 +413,25 @@ static int _tdb_unlockall(struct tdb_context *tdb, int ltype)
 /* lock entire database with write lock */
 int tdb_lockall(struct tdb_context *tdb)
 {
-       return _tdb_lockall(tdb, F_WRLCK);
+       return _tdb_lockall(tdb, F_WRLCK, F_SETLKW);
+}
+
+/* lock entire database with write lock - mark only */
+int tdb_lockall_mark(struct tdb_context *tdb)
+{
+       return _tdb_lockall(tdb, F_WRLCK | TDB_MARK_LOCK, F_SETLKW);
+}
+
+/* unlock entire database with write lock - unmark only */
+int tdb_lockall_unmark(struct tdb_context *tdb)
+{
+       return _tdb_unlockall(tdb, F_WRLCK | TDB_MARK_LOCK);
+}
+
+/* lock entire database with write lock - nonblocking varient */
+int tdb_lockall_nonblock(struct tdb_context *tdb)
+{
+       return _tdb_lockall(tdb, F_WRLCK, F_SETLK);
 }
 
 /* unlock entire database with write lock */
@@ -346,7 +443,13 @@ int tdb_unlockall(struct tdb_context *tdb)
 /* lock entire database with read lock */
 int tdb_lockall_read(struct tdb_context *tdb)
 {
-       return _tdb_lockall(tdb, F_RDLCK);
+       return _tdb_lockall(tdb, F_RDLCK, F_SETLKW);
+}
+
+/* lock entire database with read lock - nonblock varient */
+int tdb_lockall_read_nonblock(struct tdb_context *tdb)
+{
+       return _tdb_lockall(tdb, F_RDLCK, F_SETLK);
 }
 
 /* unlock entire database with read lock */
@@ -362,6 +465,26 @@ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
        return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
 }
 
+/* lock/unlock one hash chain, non-blocking. This is meant to be used
+   to reduce contention - it cannot guarantee how many records will be
+   locked */
+int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
+{
+       return tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
+}
+
+/* mark a chain as locked without actually locking it. Warning! use with great caution! */
+int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key)
+{
+       return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK);
+}
+
+/* unmark a chain as locked without actually locking it. Warning! use with great caution! */
+int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key)
+{
+       return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK);
+}
+
 int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
 {
        return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);