s3: Slightly increase parallelism in g_lock
[abartlet/samba.git/.git] / source3 / lib / g_lock.c
index 42c0397189d707154376f2db6995a379cf565523..add670c154dee1b749c92c14bc258f96fd4cadba 100644 (file)
@@ -108,6 +108,34 @@ static bool g_lock_parse(TALLOC_CTX *mem_ctx, TDB_DATA data,
                              (locks[i].lock_type & G_LOCK_PENDING) ?
                              "(pending)" : "(owner)"));
 
+               if (((locks[i].lock_type & G_LOCK_PENDING) == 0)
+                   && !process_exists(locks[i].pid)) {
+
+                       DEBUGADD(10, ("lock owner %s died -- discarding\n",
+                                     procid_str(talloc_tos(),
+                                                &locks[i].pid)));
+
+                       if (i < (num_locks-1)) {
+                               locks[i] = locks[num_locks-1];
+                       }
+                       num_locks -= 1;
+               }
+       }
+
+       *plocks = locks;
+       *pnum_locks = num_locks;
+       return true;
+}
+
+static void g_lock_cleanup(int *pnum_locks, struct g_lock_rec *locks)
+{
+       int i, num_locks;
+
+       num_locks = *pnum_locks;
+
+       DEBUG(10, ("g_lock_cleanup: %d locks\n", num_locks));
+
+       for (i=0; i<num_locks; i++) {
                if (process_exists(locks[i].pid)) {
                        continue;
                }
@@ -119,10 +147,8 @@ static bool g_lock_parse(TALLOC_CTX *mem_ctx, TDB_DATA data,
                }
                num_locks -= 1;
        }
-
-       *plocks = locks;
        *pnum_locks = num_locks;
-       return true;
+       return;
 }
 
 static struct g_lock_rec *g_lock_addrec(TALLOC_CTX *mem_ctx,
@@ -239,6 +265,13 @@ again:
                locks[our_index].lock_type = lock_type;
        }
 
+       if (NT_STATUS_IS_OK(status) && ((lock_type & G_LOCK_PENDING) == 0)) {
+               /*
+                * Walk through the list of locks, search for dead entries
+                */
+               g_lock_cleanup(&num_locks, locks);
+       }
+
        data = make_tdb_data((uint8_t *)locks, num_locks * sizeof(*locks));
        store_status = rec->store(rec, data, 0);
        if (!NT_STATUS_IS_OK(store_status)) {
@@ -265,7 +298,6 @@ NTSTATUS g_lock_lock(struct g_lock_ctx *ctx, const char *name,
        NTSTATUS status;
        bool retry = false;
        struct timeval timeout_end;
-       struct timeval timeout_remaining;
        struct timeval time_now;
 
        DEBUG(10, ("Trying to acquire lock %d for %s\n", (int)lock_type,
@@ -306,6 +338,7 @@ NTSTATUS g_lock_lock(struct g_lock_ctx *ctx, const char *name,
                fd_set *r_fds = NULL;
                int max_fd = 0;
                int ret;
+               struct timeval select_timeout;
 
                status = g_lock_trylock(ctx, name, lock_type);
                if (NT_STATUS_IS_OK(status)) {
@@ -362,12 +395,10 @@ NTSTATUS g_lock_lock(struct g_lock_ctx *ctx, const char *name,
                }
 #endif
 
-               time_now = timeval_current();
-               timeout_remaining = timeval_until(&time_now, &timeout_end);
+               select_timeout = timeval_set(60, 0);
 
                ret = sys_select(max_fd + 1, r_fds, NULL, NULL,
-                                &timeout_remaining);
-
+                                &select_timeout);
                if (ret == -1) {
                        if (errno != EINTR) {
                                DEBUG(1, ("error calling select: %s\n",
@@ -388,7 +419,7 @@ NTSTATUS g_lock_lock(struct g_lock_ctx *ctx, const char *name,
                                break;
                        } else {
                                DEBUG(10, ("select returned 0 but timeout not "
-                                          "not expired: strange - retrying\n"));
+                                          "not expired, retrying\n"));
                        }
                } else if (ret != 1) {
                        DEBUG(1, ("invalid return code of select: %d\n", ret));
@@ -496,14 +527,26 @@ static NTSTATUS g_lock_force_unlock(struct g_lock_ctx *ctx, const char *name,
                goto done;
        }
 
+       TALLOC_FREE(rec);
+
        if ((lock_type & G_LOCK_PENDING) == 0) {
+               int num_wakeups = 0;
+
                /*
-                * We've been the lock holder. Tell all others to retry.
+                * We've been the lock holder. Others to retry. Don't
+                * tell all others to avoid a thundering herd. In case
+                * this leads to a complete stall because we miss some
+                * processes, the loop in g_lock_lock tries at least
+                * once a minute.
                 */
+
                for (i=0; i<num_locks; i++) {
                        if ((locks[i].lock_type & G_LOCK_PENDING) == 0) {
                                continue;
                        }
+                       if (!process_exists(locks[i].pid)) {
+                               continue;
+                       }
 
                        /*
                         * Ping all waiters to retry
@@ -516,13 +559,22 @@ static NTSTATUS g_lock_force_unlock(struct g_lock_ctx *ctx, const char *name,
                                          procid_str(talloc_tos(),
                                                     &locks[i].pid),
                                          nt_errstr(status)));
+                       } else {
+                               num_wakeups += 1;
+                       }
+                       if (num_wakeups > 5) {
+                               break;
                        }
                }
        }
 done:
+       /*
+        * For the error path, TALLOC_FREE(rec) as well. In the good
+        * path we have already freed it.
+        */
+       TALLOC_FREE(rec);
 
        TALLOC_FREE(locks);
-       TALLOC_FREE(rec);
        return status;
 }