From: Rusty Russell Date: Tue, 22 Mar 2011 07:51:27 +0000 (+1030) Subject: tdb2: tdb_lockall() and tdb_lockall_read() support. X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=f513c5701b184fd1c0a6f03431c7fdda3ab6d2cf tdb2: tdb_lockall() and tdb_lockall_read() support. We also change tdb_chainunlock not to return an error (as per the other locking functions: we log a message, but don't return an error). --- diff --git a/ccan/tdb2/hash.c b/ccan/tdb2/hash.c index 3db1ac2e..8bda587d 100644 --- a/ccan/tdb2/hash.c +++ b/ccan/tdb2/hash.c @@ -861,7 +861,7 @@ enum TDB_ERROR tdb_chainlock(struct tdb_context *tdb, TDB_DATA key) "tdb_chainlock"); } -enum TDB_ERROR tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key) +void tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key) { uint64_t h = tdb_hash(tdb, key.dptr, key.dsize); tdb_off_t lockstart, locksize; @@ -873,6 +873,5 @@ enum TDB_ERROR tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key) lockstart = hlock_range(group, &locksize); tdb_trace_1rec(tdb, "tdb_chainunlock", key); - return tdb->last_error = tdb_unlock_hashes(tdb, lockstart, locksize, - F_WRLCK); + tdb_unlock_hashes(tdb, lockstart, locksize, F_WRLCK); } diff --git a/ccan/tdb2/lock.c b/ccan/tdb2/lock.c index 17e80d70..cad64803 100644 --- a/ccan/tdb2/lock.c +++ b/ccan/tdb2/lock.c @@ -762,7 +762,27 @@ void tdb_unlock_free_bucket(struct tdb_context *tdb, tdb_off_t b_off) tdb_nest_unlock(tdb, free_lock_off(b_off), F_WRLCK); } -void tdb_unlock_all(struct tdb_context *tdb) +enum TDB_ERROR tdb_lockall(struct tdb_context *tdb) +{ + return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false); +} + +void tdb_unlockall(struct tdb_context *tdb) +{ + tdb_allrecord_unlock(tdb, F_WRLCK); +} + +enum TDB_ERROR tdb_lockall_read(struct tdb_context *tdb) +{ + return tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false); +} + +void tdb_unlockall_read(struct tdb_context *tdb) +{ + tdb_allrecord_unlock(tdb, F_RDLCK); +} + +void tdb_lock_cleanup(struct tdb_context *tdb) { unsigned int i; diff --git a/ccan/tdb2/open.c b/ccan/tdb2/open.c index f7d6b3c5..7bb50f1d 100644 --- a/ccan/tdb2/open.c +++ b/ccan/tdb2/open.c @@ -456,7 +456,7 @@ fail_errno: #endif free(cast_const(char *, tdb->name)); if (tdb->file) { - tdb_unlock_all(tdb); + tdb_lock_cleanup(tdb); if (--tdb->file->refcnt == 0) { assert(tdb->file->num_lockrecs == 0); if (tdb->file->map_ptr) { @@ -499,7 +499,7 @@ int tdb_close(struct tdb_context *tdb) if (tdb->file) { struct tdb_file **i; - tdb_unlock_all(tdb); + tdb_lock_cleanup(tdb); if (--tdb->file->refcnt == 0) { ret = close(tdb->file->fd); diff --git a/ccan/tdb2/private.h b/ccan/tdb2/private.h index 90fe7376..a09fec24 100644 --- a/ccan/tdb2/private.h +++ b/ccan/tdb2/private.h @@ -549,7 +549,7 @@ enum TDB_ERROR tdb_unlock_hashes(struct tdb_context *tdb, tdb_len_t hash_range, int ltype); /* For closing the file. */ -void tdb_unlock_all(struct tdb_context *tdb); +void tdb_lock_cleanup(struct tdb_context *tdb); /* Lock/unlock a particular free bucket. */ enum TDB_ERROR tdb_lock_free_bucket(struct tdb_context *tdb, tdb_off_t b_off, diff --git a/ccan/tdb2/tdb2.h b/ccan/tdb2/tdb2.h index fa5787c3..52c234f1 100644 --- a/ccan/tdb2/tdb2.h +++ b/ccan/tdb2/tdb2.h @@ -411,8 +411,43 @@ enum TDB_ERROR tdb_chainlock(struct tdb_context *tdb, TDB_DATA key); * tdb_chainunlock - unlock a record in the TDB * @tdb: the tdb context returned from tdb_open() * @key: the key to unlock. + * + * The key must have previously been locked by tdb_chainlock(). + */ +void tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key); + +/** + * tdb_lockall - lock the entire TDB + * @tdb: the tdb context returned from tdb_open() + * + * You cannot hold a tdb_chainlock while calling this. It nests, so you + * must call tdb_unlockall as many times as you call tdb_lockall. + */ +enum TDB_ERROR tdb_lockall(struct tdb_context *tdb); + +/** + * tdb_unlockall - unlock the entire TDB + * @tdb: the tdb context returned from tdb_open() + */ +void tdb_unlockall(struct tdb_context *tdb); + +/** + * tdb_lockall_read - lock the entire TDB for reading + * @tdb: the tdb context returned from tdb_open() + * + * This prevents others writing to the database, eg. tdb_delete, tdb_store, + * tdb_append, but not tdb_fetch. + * + * You cannot hold a tdb_chainlock while calling this. It nests, so you + * must call tdb_unlockall_read as many times as you call tdb_lockall_read. + */ +enum TDB_ERROR tdb_lockall_read(struct tdb_context *tdb); + +/** + * tdb_unlockall_read - unlock the entire TDB for reading + * @tdb: the tdb context returned from tdb_open() */ -enum TDB_ERROR tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key); +void tdb_unlockall_read(struct tdb_context *tdb); /** * tdb_wipe_all - wipe the database clean diff --git a/ccan/tdb2/test/run-lockall.c b/ccan/tdb2/test/run-lockall.c new file mode 100644 index 00000000..4aedf597 --- /dev/null +++ b/ccan/tdb2/test/run-lockall.c @@ -0,0 +1,80 @@ +#include "config.h" +#include +#include "lock-tracking.h" + +#define fcntl fcntl_with_lockcheck + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "external-agent.h" +#include "logging.h" + +#define TEST_DBNAME "run-lockall.tdb" + +#undef fcntl + +int main(int argc, char *argv[]) +{ + struct agent *agent; + const int flags[] = { TDB_DEFAULT, + TDB_NOMMAP, + TDB_CONVERT, + TDB_CONVERT | TDB_NOMMAP }; + int i; + + plan_tests(13 * sizeof(flags)/sizeof(flags[0]) + 1); + agent = prepare_external_agent(); + if (!agent) + err(1, "preparing agent"); + + for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) { + enum agent_return ret; + struct tdb_context *tdb; + + tdb = tdb_open(TEST_DBNAME, flags[i], + O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr); + ok1(tdb); + + ret = external_agent_operation(agent, OPEN, TEST_DBNAME); + ok1(ret == SUCCESS); + + ok1(tdb_lockall(tdb) == TDB_SUCCESS); + ok1(external_agent_operation(agent, STORE, "key") + == WOULD_HAVE_BLOCKED); + ok1(external_agent_operation(agent, FETCH, "key") + == WOULD_HAVE_BLOCKED); + /* Test nesting. */ + ok1(tdb_lockall(tdb) == TDB_SUCCESS); + tdb_unlockall(tdb); + tdb_unlockall(tdb); + + ok1(external_agent_operation(agent, STORE, "key") == SUCCESS); + + ok1(tdb_lockall_read(tdb) == TDB_SUCCESS); + ok1(external_agent_operation(agent, STORE, "key") + == WOULD_HAVE_BLOCKED); + ok1(external_agent_operation(agent, FETCH, "key") == SUCCESS); + ok1(tdb_lockall_read(tdb) == TDB_SUCCESS); + tdb_unlockall_read(tdb); + tdb_unlockall_read(tdb); + + ok1(external_agent_operation(agent, STORE, "key") == SUCCESS); + ok1(external_agent_operation(agent, CLOSE, NULL) == SUCCESS); + tdb_close(tdb); + } + + free_external_agent(agent); + ok1(tap_log_messages == 0); + return exit_status(); +}