summaryrefslogtreecommitdiff
path: root/source3/tdb/common/lock.c
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2006-10-20 09:55:47 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:15:35 -0500
commit7d52581978c7f95e18a3c5b5c38adc486af63023 (patch)
tree00515ad71cd84b3cf6a799ae1a535a9b3d0849c1 /source3/tdb/common/lock.c
parent3da46073748c21a9202575883f3a57dacb2882ee (diff)
downloadsamba-7d52581978c7f95e18a3c5b5c38adc486af63023.tar.gz
samba-7d52581978c7f95e18a3c5b5c38adc486af63023.tar.bz2
samba-7d52581978c7f95e18a3c5b5c38adc486af63023.zip
r19426: merge nearly all the differences between Samba3 tdb and Samba4
tdb. This includes: - the new tdb_lockall and tdb_lockall_read code, which will be needed for the ldb speedups - the tdb logging changes. This is an intermediate step to keep the differences between the two branches small. The plan is still to move to a tdb_init()/tdb_set_logging_function()/tdb_attach() style of open which will make things much cleaner. - the updated test suites and standalone tdb build code - use libreplace headers There are still some small differences I haven't merged. I'll discuss those on the list. (This used to be commit 48903c75edfaf75dbd3e9d052e615552cdff39b4)
Diffstat (limited to 'source3/tdb/common/lock.c')
-rw-r--r--source3/tdb/common/lock.c152
1 files changed, 109 insertions, 43 deletions
diff --git a/source3/tdb/common/lock.c b/source3/tdb/common/lock.c
index a96d77a229..a5bff2d0b3 100644
--- a/source3/tdb/common/lock.c
+++ b/source3/tdb/common/lock.c
@@ -36,8 +36,8 @@
note that a len of zero means lock to end of file
*/
-int tdb_brlock_len(struct tdb_context *tdb, tdb_off_t offset,
- int rw_type, int lck_type, int probe, size_t len)
+int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset,
+ int rw_type, int lck_type, int probe, size_t len)
{
struct flock fl;
int ret;
@@ -68,7 +68,7 @@ int tdb_brlock_len(struct tdb_context *tdb, tdb_off_t offset,
if (!probe && lck_type != F_SETLK) {
/* Ensure error code is set for log fun to examine. */
tdb->ecode = TDB_ERR_LOCK;
- TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n",
+ TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n",
tdb->fd, offset, rw_type, lck_type, (int)len));
}
return TDB_ERRCODE(TDB_ERR_LOCK, -1);
@@ -88,7 +88,7 @@ int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len)
int count = 1000;
while (count--) {
struct timeval tv;
- if (tdb_brlock_len(tdb, offset, F_WRLCK, F_SETLKW, 1, len) == 0) {
+ if (tdb_brlock(tdb, offset, F_WRLCK, F_SETLKW, 1, len) == 0) {
return 0;
}
if (errno != EDEADLK) {
@@ -99,27 +99,26 @@ int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len)
tv.tv_usec = 1;
select(0, NULL, NULL, NULL, &tv);
}
- TDB_LOG((tdb, 5,"tdb_brlock_upgrade failed at offset %d\n", offset));
+ TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock_upgrade failed at offset %d\n", offset));
return -1;
}
-/* a byte range locking function - return 0 on success
- this functions locks/unlocks 1 byte at the specified offset.
-
- On error, errno is also set so that errors are passed back properly
- through tdb_open(). */
-int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset,
- int rw_type, int lck_type, int probe)
-{
- return tdb_brlock_len(tdb, offset, rw_type, lck_type, probe, 1);
-}
-
/* lock a list in the database. list -1 is the alloc list */
int tdb_lock(struct tdb_context *tdb, int list, int ltype)
{
+ /* a global lock allows us to avoid per chain locks */
+ if (tdb->global_lock.count &&
+ (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) {
+ return 0;
+ }
+
+ if (tdb->global_lock.count) {
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+
if (list < -1 || list >= (int)tdb->header.hash_size) {
- TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n",
+ TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid list %d for ltype=%d\n",
list, ltype));
return -1;
}
@@ -129,8 +128,8 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype)
/* 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)) {
- TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n",
+ 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;
}
@@ -148,23 +147,33 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
{
int ret = -1;
+ /* a global lock allows us to avoid per chain locks */
+ if (tdb->global_lock.count &&
+ (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) {
+ return 0;
+ }
+
+ if (tdb->global_lock.count) {
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+
if (tdb->flags & TDB_NOLOCK)
return 0;
/* Sanity checks */
if (list < -1 || list >= (int)tdb->header.hash_size) {
- TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
return ret;
}
if (tdb->locked[list+1].count==0) {
- TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n"));
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n"));
return ret;
}
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);
+ ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0, 1);
tdb->num_locks--;
} else {
ret = 0;
@@ -172,40 +181,97 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
tdb->locked[list+1].count--;
if (ret)
- TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n"));
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n"));
return ret;
}
/* lock/unlock entire database */
-int tdb_lockall(struct tdb_context *tdb)
+static int _tdb_lockall(struct tdb_context *tdb, int ltype)
{
- u32 i;
-
/* There are no locks on read-only dbs */
if (tdb->read_only || tdb->traverse_read)
return TDB_ERRCODE(TDB_ERR_LOCK, -1);
- for (i = 0; i < tdb->header.hash_size; i++)
- if (tdb_lock(tdb, i, F_WRLCK))
- break;
- /* If error, release locks we have... */
- if (i < tdb->header.hash_size) {
- u32 j;
+ if (tdb->global_lock.count && tdb->global_lock.ltype == ltype) {
+ tdb->global_lock.count++;
+ return 0;
+ }
+
+ if (tdb->global_lock.count) {
+ /* a global lock of a different type exists */
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+
+ if (tdb->num_locks != 0) {
+ /* can't combine global and chain locks */
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+
+ if (tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, F_SETLKW,
+ 0, 4*tdb->header.hash_size)) {
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno)));
+ return -1;
+ }
+
+ tdb->global_lock.count = 1;
+ tdb->global_lock.ltype = ltype;
+
+ return 0;
+}
+
+/* unlock entire db */
+static int _tdb_unlockall(struct tdb_context *tdb, int ltype)
+{
+ /* There are no locks on read-only dbs */
+ if (tdb->read_only || tdb->traverse_read) {
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
- for ( j = 0; j < i; j++)
- tdb_unlock(tdb, j, F_WRLCK);
- return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+ if (tdb->global_lock.ltype != ltype || tdb->global_lock.count == 0) {
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+
+ if (tdb->global_lock.count > 1) {
+ tdb->global_lock.count--;
+ return 0;
}
+ if (tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW,
+ 0, 4*tdb->header.hash_size)) {
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno)));
+ return -1;
+ }
+
+ tdb->global_lock.count = 0;
+ tdb->global_lock.ltype = 0;
+
return 0;
}
-void tdb_unlockall(struct tdb_context *tdb)
+
+/* lock entire database with write lock */
+int tdb_lockall(struct tdb_context *tdb)
+{
+ return _tdb_lockall(tdb, F_WRLCK);
+}
+
+/* unlock entire database with write lock */
+int tdb_unlockall(struct tdb_context *tdb)
+{
+ return _tdb_unlockall(tdb, F_WRLCK);
+}
+
+/* lock entire database with read lock */
+int tdb_lockall_read(struct tdb_context *tdb)
+{
+ return _tdb_lockall(tdb, F_RDLCK);
+}
+
+/* unlock entire database with read lock */
+int tdb_unlockall_read(struct tdb_context *tdb)
{
- u32 i;
- for (i=0; i < tdb->header.hash_size; i++)
- tdb_unlock(tdb, i, F_WRLCK);
+ return _tdb_unlockall(tdb, F_RDLCK);
}
/* lock/unlock one hash chain. This is meant to be used to reduce
@@ -235,7 +301,7 @@ int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
/* record lock stops delete underneath */
int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off)
{
- return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0;
+ return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0, 1) : 0;
}
/*
@@ -249,7 +315,7 @@ int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off)
for (i = &tdb->travlocks; i; i = i->next)
if (i->off == off)
return -1;
- return tdb->methods->tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1);
+ return tdb->methods->tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1, 1);
}
/*
@@ -258,7 +324,7 @@ int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off)
*/
int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off)
{
- return tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0);
+ return tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0, 1);
}
/* fcntl locks don't stack: avoid unlocking someone else's */
@@ -272,5 +338,5 @@ int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off)
for (i = &tdb->travlocks; i; i = i->next)
if (i->off == off)
count++;
- return (count == 1 ? tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0);
+ return (count == 1 ? tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0, 1) : 0);
}