strsplit: remove nump argument
[ccan] / ccan / tdb / test / lock-tracking.c
index bc4be14759211f6718bfda34d124652329d3544a..8e8bb999c52df90ce01a444b4d987b511b2c2e16 100644 (file)
@@ -4,6 +4,8 @@
 #include <stdarg.h>
 #include <stdlib.h>
 #include <ccan/tap/tap.h>
+#include <ccan/tdb/tdb_private.h>
+#include "lock-tracking.h"
 
 struct lock {
        struct lock *next;
@@ -13,6 +15,9 @@ struct lock {
 };
 static struct lock *locks;
 int locking_errors = 0;
+bool suppress_lockcheck = false;
+bool nonblocking_locks;
+int locking_would_block = 0;
 void (*unlock_callback)(int fd);
 
 int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
@@ -20,6 +25,7 @@ int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
        va_list ap;
        int ret, arg3;
        struct flock *fl;
+       bool may_block = false;
 
        if (cmd != F_SETLK && cmd != F_SETLKW) {
                /* This may be totally bogus, but we don't know in general. */
@@ -34,6 +40,17 @@ int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
        fl = va_arg(ap, struct flock *);
        va_end(ap);
 
+       if (cmd == F_SETLKW && nonblocking_locks) {
+               cmd = F_SETLK;
+               may_block = true;
+       }
+       ret = fcntl(fd, cmd, fl);
+
+       /* Detect when we failed, but might have been OK if we waited. */
+       if (may_block && ret == -1 && (errno == EAGAIN || errno == EACCES)) {
+               locking_would_block++;
+       }
+
        if (fl->l_type == F_UNLCK) {
                struct lock **l;
                struct lock *old = NULL;
@@ -41,15 +58,17 @@ int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
                for (l = &locks; *l; l = &(*l)->next) {
                        if ((*l)->off == fl->l_start
                            && (*l)->len == fl->l_len) {
-                               old = *l;
-                               *l = (*l)->next;
-                               free(old);
+                               if (ret == 0) {
+                                       old = *l;
+                                       *l = (*l)->next;
+                                       free(old);
+                               }
                                break;
                        }
                }
-               if (!old) {
-                       diag("Unknown unlock %u@%u",
-                            (int)fl->l_len, (int)fl->l_start);
+               if (!old && !suppress_lockcheck) {
+                       diag("Unknown unlock %u@%u - %i",
+                            (int)fl->l_len, (int)fl->l_start, ret);
                        locking_errors++;
                }
        } else {
@@ -68,29 +87,53 @@ int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
                                break;
                        if (fl_end >= i->off && fl_end < i_end)
                                break;
+
+                       /* tdb_allrecord_lock does this, handle adjacent: */
+                       if (fl->l_start == i_end && fl->l_type == i->type) {
+                               if (ret == 0) {
+                                       i->len = fl->l_len 
+                                               ? i->len + fl->l_len
+                                               : 0;
+                               }
+                               goto done;
+                       }
                }
                if (i) {
-                       diag("%s lock %u@%u overlaps %u@%u",
-                            fl->l_type == F_WRLCK ? "write" : "read",
-                            (int)fl->l_len, (int)fl->l_start,
-                            i->len, (int)i->off);
-                       locking_errors++;
+                       /* Special case: upgrade of allrecord lock. */
+                       if (i->type == F_RDLCK && fl->l_type == F_WRLCK
+                           && i->off == FREELIST_TOP
+                           && fl->l_start == FREELIST_TOP
+                           && i->len == 0
+                           && fl->l_len == 0) {
+                               if (ret == 0)
+                                       i->type = F_WRLCK;
+                               goto done;
+                       }
+                       if (!suppress_lockcheck) {
+                               diag("%s lock %u@%u overlaps %u@%u",
+                                    fl->l_type == F_WRLCK ? "write" : "read",
+                                    (int)fl->l_len, (int)fl->l_start,
+                                    i->len, (int)i->off);
+                               locking_errors++;
+                       }
                }
-               new = malloc(sizeof *new);
-               new->off = fl->l_start;
-               new->len = fl->l_len;
-               new->type = fl->l_type;
-               new->next = locks;
-               locks = new;
-       }
 
-       ret = fcntl(fd, cmd, fl);
+               if (ret == 0) {
+                       new = malloc(sizeof *new);
+                       new->off = fl->l_start;
+                       new->len = fl->l_len;
+                       new->type = fl->l_type;
+                       new->next = locks;
+                       locks = new;
+               }
+       }
+done:
        if (ret == 0 && fl->l_type == F_UNLCK && unlock_callback)
                unlock_callback(fd);
        return ret;
 }
 
-int forget_locking(void)
+unsigned int forget_locking(void)
 {
        unsigned int num = 0;
        while (locks) {