r21303: As discussed on samba-technical: Change the static array for the in-memory
authorVolker Lendecke <vlendec@samba.org>
Mon, 12 Feb 2007 23:16:02 +0000 (23:16 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 19:48:06 +0000 (14:48 -0500)
mirrors of the hash chain locks to a dynamically allocated one.

Jeremy, I count on you to revert it if the build farm freaks out, it's after
midnight here :-)

Volker
(This used to be commit 7b5db2e472c7e27231fa432d3930789e708abd09)

source4/lib/tdb/common/lock.c
source4/lib/tdb/common/open.c
source4/lib/tdb/common/tdb_private.h
source4/lib/tdb/common/transaction.c

index a5bff2d0b31ba102993386164d999958fe15e644..8a964371d3aa846510c90edc7e95a7c7c11eec67 100644 (file)
@@ -107,6 +107,9 @@ 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)
 {
+       struct tdb_lock_type *new_lck;
+       int i;
+
        /* a global lock allows us to avoid per chain locks */
        if (tdb->global_lock.count && 
            (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) {
@@ -125,18 +128,50 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype)
        if (tdb->flags & TDB_NOLOCK)
                return 0;
 
+       for (i=0; i<tdb->num_lockrecs; i++) {
+               if (tdb->lockrecs[i].list == list) {
+                       if (tdb->lockrecs[i].count == 0) {
+                               /*
+                                * Can't happen, see tdb_unlock(). It should
+                                * be an assert.
+                                */
+                               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock: "
+                                        "lck->count == 0 for list %d", list));
+                       }
+                       /*
+                        * Just increment the in-memory struct, posix locks
+                        * don't stack.
+                        */
+                       tdb->lockrecs[i].count++;
+                       return 0;
+               }
+       }
+
+       new_lck = (struct tdb_lock_type *)realloc(
+               tdb->lockrecs,
+               sizeof(*tdb->lockrecs) * (tdb->num_lockrecs+1));
+       if (new_lck == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+       tdb->lockrecs = new_lck;
+
        /* Since fcntl locks don't nest, we do a lock for the first one,
           and simply bump the count for future ones */
-       if (tdb->locked[list+1].count == 0) {
-               if (tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0, 1)) {
-                       TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d ltype=%d (%s)\n", 
-                                list, ltype, strerror(errno)));
-                       return -1;
-               }
-               tdb->locked[list+1].ltype = ltype;
-               tdb->num_locks++;
+       if (tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW,
+                                    0, 1)) {
+               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d "
+                        "ltype=%d (%s)\n",  list, ltype, strerror(errno)));
+               return -1;
        }
-       tdb->locked[list+1].count++;
+
+       tdb->num_locks++;
+
+       tdb->lockrecs[tdb->num_lockrecs].list = list;
+       tdb->lockrecs[tdb->num_lockrecs].count = 1;
+       tdb->lockrecs[tdb->num_lockrecs].ltype = ltype;
+       tdb->num_lockrecs += 1;
+
        return 0;
 }
 
@@ -146,6 +181,8 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype)
 int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
 {
        int ret = -1;
+       int i;
+       struct tdb_lock_type *lck = NULL;
 
        /* a global lock allows us to avoid per chain locks */
        if (tdb->global_lock.count && 
@@ -166,19 +203,52 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
                return ret;
        }
 
-       if (tdb->locked[list+1].count==0) {
+       for (i=0; i<tdb->num_lockrecs; i++) {
+               if (tdb->lockrecs[i].list == list) {
+                       lck = &tdb->lockrecs[i];
+                       break;
+               }
+       }
+
+       if ((lck == NULL) || (lck->count == 0)) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n"));
-               return ret;
+               return -1;
+       }
+
+       if (lck->count > 1) {
+               lck->count--;
+               return 0;
        }
 
-       if (tdb->locked[list+1].count == 1) {
-               /* Down to last nested lock: unlock underneath */
-               ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0, 1);
-               tdb->num_locks--;
-       } else {
-               ret = 0;
+       /*
+        * This lock has count==1 left, so we need to unlock it in the
+        * kernel. We don't bother with decrementing the in-memory array
+        * element, we're about to overwrite it with the last array element
+        * anyway.
+        */
+
+       ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK,
+                                      F_SETLKW, 0, 1);
+       tdb->num_locks--;
+
+       /*
+        * Shrink the array by overwriting the element just unlocked with the
+        * last array element.
+        */
+
+       if (tdb->num_lockrecs > 1) {
+               *lck = tdb->lockrecs[tdb->num_lockrecs-1];
+       }
+       tdb->num_lockrecs -= 1;
+
+       /*
+        * We don't bother with realloc when the array shrinks, but if we have
+        * a completely idle tdb we should get rid of the locked array.
+        */
+
+       if (tdb->num_lockrecs == 0) {
+               SAFE_FREE(tdb->lockrecs);
        }
-       tdb->locked[list+1].count--;
 
        if (ret)
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n")); 
index e1f21aa8560b3e02bfadc2e0a61914de30f5f7da..3d6e222b96915055c73d96ee78c3d4cef5527950 100644 (file)
@@ -263,15 +263,6 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
        tdb->map_size = st.st_size;
        tdb->device = st.st_dev;
        tdb->inode = st.st_ino;
-       tdb->locked = (struct tdb_lock_type *)calloc(tdb->header.hash_size+1,
-                                                    sizeof(tdb->locked[0]));
-       if (!tdb->locked) {
-               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
-                        "failed to allocate lock structure for %s\n",
-                        name));
-               errno = ENOMEM;
-               goto fail;
-       }
        tdb_mmap(tdb);
        if (locked) {
                if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) {
@@ -324,7 +315,6 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
        if (tdb->fd != -1)
                if (close(tdb->fd) != 0)
                        TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n"));
-       SAFE_FREE(tdb->locked);
        SAFE_FREE(tdb);
        errno = save_errno;
        return NULL;
@@ -354,7 +344,7 @@ int tdb_close(struct tdb_context *tdb)
        SAFE_FREE(tdb->name);
        if (tdb->fd != -1)
                ret = close(tdb->fd);
-       SAFE_FREE(tdb->locked);
+       SAFE_FREE(tdb->lockrecs);
 
        /* Remove from contexts list */
        for (i = &tdbs; *i; i = &(*i)->next) {
index a3495e42aafba646765d77c94bc2bc2b9fee206c..527785794213c0eeda68b4b1bbbabc07bd7cab88 100644 (file)
@@ -123,6 +123,7 @@ struct tdb_header {
 };
 
 struct tdb_lock_type {
+       int list;
        u32 count;
        u32 ltype;
 };
@@ -152,7 +153,8 @@ struct tdb_context {
        int read_only; /* opened read-only */
        int traverse_read; /* read-only traversal */
        struct tdb_lock_type global_lock;
-       struct tdb_lock_type *locked; /* array of chain locks */
+       int num_lockrecs;
+       struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */
        enum TDB_ERROR ecode; /* error code for last tdb error */
        struct tdb_header header; /* a cached copy of the header */
        u32 flags; /* the flags passed to tdb_open */
index 7dff9a95e345ec9df1ce9c07adb537a9ac18cfaa..5c94eb0afef991a8f87a4dbacfa414d747fee33c 100644 (file)
@@ -516,12 +516,10 @@ int tdb_transaction_cancel(struct tdb_context *tdb)
 
        /* remove any locks created during the transaction */
        if (tdb->num_locks != 0) {
-               int h;
-               for (h=0;h<tdb->header.hash_size+1;h++) {
-                       if (tdb->locked[h].count != 0) {
-                               tdb_brlock(tdb,FREELIST_TOP+4*h,F_UNLCK,F_SETLKW, 0, 1);
-                               tdb->locked[h].count = 0;
-                       }
+               int i;
+               for (i=0;i<tdb->num_lockrecs;i++) {
+                       tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list,
+                                  F_UNLCK,F_SETLKW, 0, 1);
                }
                tdb->num_locks = 0;
        }