[PATCH] lib/tdb: new locking hook.

Rusty Russell rusty at rustcorp.com.au
Thu Aug 27 20:29:12 MDT 2009


This obsoletes the *mark and *nonblock calls, by allowing the user
to do fancy work replacing, recording or faking fcntl locks.  This
should make ctdb's life easier.  Clumsily, this also fixes a shadow
warning (wait) introduced by previous patch.

(Note tdb_increment_seqnum_nonblock is not obsoleted, since despite
 the name it simply operates lockless).

We could avoid adding YA open function if we didn't want to allow
non-blocking open, or complete tracking of all lock operations.

Signed-off-by: Rusty Russell <rusty at rustcorp.com.au>
---
 lib/tdb/common/lock.c        |   43 +++++++++++++++++++++++++++++++++++++----
 lib/tdb/common/open.c        |   28 ++++++++++++++++++++++++++-
 lib/tdb/common/tdb_private.h |    2 +
 lib/tdb/docs/README          |   32 +++++++++++++++++++++++++++++++
 lib/tdb/include/tdb.h        |   24 +++++++++++++++++++++++
 5 files changed, 123 insertions(+), 6 deletions(-)

diff --git a/lib/tdb/common/lock.c b/lib/tdb/common/lock.c
index 493012f..86b7f3b 100644
--- a/lib/tdb/common/lock.c
+++ b/lib/tdb/common/lock.c
@@ -33,7 +33,7 @@ void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
 }
 
 static int fcntl_lock(struct tdb_context *tdb,
-		      int rw, off_t off, off_t len, bool wait)
+		      int rw, off_t off, off_t len, bool lwait)
 {
 	struct flock fl;
 	
@@ -43,7 +43,7 @@ static int fcntl_lock(struct tdb_context *tdb,
 	fl.l_len = len;
 	fl.l_pid = 0;
 
-	if (wait)
+	if (lwait)
 		return fcntl(tdb->fd, F_SETLKW, &fl);
 	else
 		return fcntl(tdb->fd, F_SETLK, &fl);
@@ -119,6 +119,39 @@ static int fcntl_unlock(struct tdb_context *tdb, int rw, off_t off, off_t len)
 	return fcntl(tdb->fd, F_SETLKW, &fl);
 }
 
+static void *noop_init_locks(struct tdb_context *tdb)
+{
+	/* any non-NULL pointer will work; we don't use it anyway. */
+	return tdb;
+}
+
+static void noop_destroy_locks(struct tdb_context *tdb)
+{
+}
+
+const struct tdb_lock_methods tdb_default_lock_methods = {
+	.init_locks = noop_init_locks,
+	.destroy_locks = noop_destroy_locks,
+	.lock = fcntl_lock,
+	.unlock = fcntl_unlock,
+};
+
+const struct tdb_lock_methods *tdb_get_lock_methods(struct tdb_context *tdb,
+						     void **lock_data)
+{
+	if (lock_data)
+		*lock_data = tdb->lock_data;
+	return tdb->lock_methods;
+}
+
+void tdb_set_lock_methods(struct tdb_context *tdb,
+			  const struct tdb_lock_methods *lock_methods,
+			  void *lock_data)
+{
+	tdb->lock_methods = lock_methods;
+	tdb->lock_data = lock_data;
+}
+
 /* a byte range locking function - return 0 on success
    this functions locks/unlocks 1 byte at the specified offset.
 
@@ -147,8 +180,8 @@ int tdb_brlock(struct tdb_context *tdb,
 	}
 
 	do {
-		ret = fcntl_lock(tdb, rw_type, offset, len,
-				 flags & TDB_LOCK_WAIT);
+		ret = tdb->lock_methods->lock(tdb, rw_type, offset, len,
+					      flags & TDB_LOCK_WAIT);
 		/* Check for a sigalarm break. */
 		if (ret == -1 && errno == EINTR &&
 				tdb->interrupt_sig_ptr &&
@@ -181,7 +214,7 @@ int tdb_brunlock(struct tdb_context *tdb,
 	}
 
 	do {
-		ret = fcntl_unlock(tdb, rw_type, offset, len);
+		ret = tdb->lock_methods->unlock(tdb, rw_type, offset, len);
 	} while (ret == -1 && errno == EINTR);
 
 	if (ret == -1) {
diff --git a/lib/tdb/common/open.c b/lib/tdb/common/open.c
index 76c67aa..b5e7de0 100644
--- a/lib/tdb/common/open.c
+++ b/lib/tdb/common/open.c
@@ -134,7 +134,7 @@ static int tdb_already_open(dev_t device,
 struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
 		      int open_flags, mode_t mode)
 {
-	return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL);
+	return tdb_open_exl(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL, &tdb_default_lock_methods);
 }
 
 /* a default logging function */
@@ -149,6 +149,16 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
 				const struct tdb_logging_context *log_ctx,
 				tdb_hash_func hash_fn)
 {
+	return tdb_open_exl(name, hash_size, tdb_flags, open_flags, mode,
+			    log_ctx, hash_fn, &tdb_default_lock_methods);
+}
+
+struct tdb_context *tdb_open_exl(const char *name, int hash_size, int tdb_flags,
+				 int open_flags, mode_t mode,
+				 const struct tdb_logging_context *log_ctx,
+				 tdb_hash_func hash_fn,
+				 const struct tdb_lock_methods *lock_methods)
+{
 	struct tdb_context *tdb;
 	struct stat st;
 	int rev = 0, locked = 0;
@@ -224,6 +234,12 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
 	v = fcntl(tdb->fd, F_GETFD, 0);
         fcntl(tdb->fd, F_SETFD, v | FD_CLOEXEC);
 
+	/* initialize locking. */
+	tdb->lock_methods = lock_methods;
+	tdb->lock_data = lock_methods->init_locks(tdb);
+	if (!tdb->lock_data)
+		goto fail;
+
 	/* ensure there is only one process initialising at once */
 	if (tdb->methods->brlock(tdb, F_WRLCK, GLOBAL_LOCK, 1, TDB_LOCK_WAIT) == -1) {
 		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get global lock on %s: %s\n",
@@ -355,6 +371,9 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
 #ifdef TDB_TRACE
 	close(tdb->tracefd);
 #endif
+	if (tdb->lock_data)
+		tdb->lock_methods->destroy_locks(tdb);
+
 	if (tdb->map_ptr) {
 		if (tdb->flags & TDB_INTERNAL)
 			SAFE_FREE(tdb->map_ptr);
@@ -481,6 +500,13 @@ static int tdb_reopen_internal(struct tdb_context *tdb, bool active_lock)
 	tdb_mmap(tdb);
 #endif /* fake pread or pwrite */
 
+	tdb->lock_methods->destroy_locks(tdb);
+	tdb->lock_data = tdb->lock_methods->init_locks(tdb);
+	if (!tdb->lock_data) {
+		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to re-initialize locks\n"));
+		goto fail;
+	}
+
 	if (active_lock &&
 	    (tdb->methods->brlock(tdb, F_RDLCK, ACTIVE_LOCK, 1, TDB_LOCK_WAIT) == -1)) {
 		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n"));
diff --git a/lib/tdb/common/tdb_private.h b/lib/tdb/common/tdb_private.h
index 28b88af..fdedf0b 100644
--- a/lib/tdb/common/tdb_private.h
+++ b/lib/tdb/common/tdb_private.h
@@ -198,6 +198,8 @@ struct tdb_context {
 	uint32_t flags; /* the flags passed to tdb_open */
 	struct tdb_traverse_lock travlocks; /* current traversal locks */
 	struct tdb_context *next; /* all tdbs to avoid multiple opens */
+	const struct tdb_lock_methods *lock_methods;
+	void *lock_data;
 	dev_t device;	/* uniquely identifies this tdb */
 	ino_t inode;	/* uniquely identifies this tdb */
 	struct tdb_logging_context log;
diff --git a/lib/tdb/docs/README b/lib/tdb/docs/README
index 454e4ba..6fcdf8f 100644
--- a/lib/tdb/docs/README
+++ b/lib/tdb/docs/README
@@ -81,6 +81,38 @@ hash function. Be careful when passing a hash function - all users of
 the database must use the same hash function or you will get data
 corruption.
 
+----------------------------------------------------------------------
+TDB_CONTEXT *tdb_open_exl(char *name, int hash_size, int tdb_flags,
+			  int open_flags, mode_t mode,
+			  tdb_log_func log_fn,
+			  tdb_hash_func hash_fn,
+			  const struct tdb_lock_methods *lock_methods)
+
+This is like tdb_open_ex(), but includes a set of locking functions
+which can be used to enhance or override the defaults.  This has several
+methods:
+
+	void *(*init_locks)(struct tdb_context *tdb);
+
+This is called from tdb_open_exl, and also from tdb_reopen/tdb_reopen_all.
+It returns a non-NULL pointer on success, which can be accessed through
+tdb_get_lock_methods().
+
+	void (*destroy_locks)(struct tdb_context *tdb);
+
+This is called from tdb_close, and also tdb_reopen/tdb_reopen_all.
+
+	int (*lock)(struct tdb_context *tdb, int rw, off_t off, off_t len,
+		    bool wait);
+	int (*unlock)(struct tdb_context *tdb, int rw, off_t off, off_t len);
+
+These two functions are expected to place a fcntl-style lock on the tdb_fd().
+rw is either F_RDLCK or F_WRLCK, off is the offset from the start of the file,
+and len is the length: a len of 0 means "to infinity".  They return 0 on
+success, -1 on error.  The (successful) calls to lock and unlock should be 
+symmetrical, but a robust implementation should detect locking errors.
+
+The default tdb_lock_methods can be found in "tdb_default_lock_methods".
 
 ----------------------------------------------------------------------
 char *tdb_error(TDB_CONTEXT *tdb);
diff --git a/lib/tdb/include/tdb.h b/lib/tdb/include/tdb.h
index c00b6cb..bcc0b0d 100644
--- a/lib/tdb/include/tdb.h
+++ b/lib/tdb/include/tdb.h
@@ -87,12 +87,31 @@ struct tdb_logging_context {
         void *log_private;
 };
 
+struct tdb_lock_methods {
+	/* Called from tdb_open* and tdb_close */
+	void *(*init_locks)(struct tdb_context *tdb);
+	void (*destroy_locks)(struct tdb_context *tdb);
+
+	/* Grab a lock on the file, fcntl-style. */
+	int (*lock)(struct tdb_context *tdb, int rw, off_t off, off_t len,
+		    bool wait);
+	int (*unlock)(struct tdb_context *tdb, int rw, off_t off, off_t len);
+};
+
+/* The default locking methods. */
+extern const struct tdb_lock_methods tdb_default_lock_methods;
+
 struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
 		      int open_flags, mode_t mode);
 struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
 			 int open_flags, mode_t mode,
 			 const struct tdb_logging_context *log_ctx,
 			 tdb_hash_func hash_fn);
+struct tdb_context *tdb_open_exl(const char *name, int hash_size, int tdb_flags,
+			 int open_flags, mode_t mode,
+			 const struct tdb_logging_context *log_ctx,
+			 tdb_hash_func hash_fn,
+			 const struct tdb_lock_methods *lock_methods);
 void tdb_set_max_dead(struct tdb_context *tdb, int max_dead);
 
 int tdb_reopen(struct tdb_context *tdb);
@@ -148,6 +167,11 @@ int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key);
 int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key);
 int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key);
 int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key);
+const struct tdb_lock_methods *tdb_get_lock_methods(struct tdb_context *tdb,
+						     void **lock_data);
+void tdb_set_lock_methods(struct tdb_context *tdb,
+			  const struct tdb_lock_methods *lock_methods,
+			  void *lock_data);
 
 void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *sigptr);
 
-- 
1.6.0.4



More information about the samba-technical mailing list