From: Timo Sirainen Date: Sun, 20 Jun 2004 11:17:53 +0000 (+0300) Subject: Allow dropping exclusive mbox locks to shared locks. X-Git-Tag: 1.1.alpha1~3900 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2c7ab05ef98c46eb70c8ba6ea85e49749aafb2a3;p=thirdparty%2Fdovecot%2Fcore.git Allow dropping exclusive mbox locks to shared locks. --HG-- branch : HEAD --- diff --git a/src/lib-storage/index/index-storage.h b/src/lib-storage/index/index-storage.h index 82cf4f59d8..f4e803aff6 100644 --- a/src/lib-storage/index/index-storage.h +++ b/src/lib-storage/index/index-storage.h @@ -84,7 +84,7 @@ struct index_mailbox { int mbox_lock_type; dev_t mbox_dev; ino_t mbox_ino; - unsigned int mbox_locks; + unsigned int mbox_excl_locks, mbox_shared_locks; struct dotlock mbox_dotlock; unsigned int mbox_lock_id, mbox_mail_lock_id; int mbox_readonly; diff --git a/src/lib-storage/index/mbox/mbox-lock.c b/src/lib-storage/index/mbox/mbox-lock.c index 2b9fd4a00b..b899e3b42e 100644 --- a/src/lib-storage/index/mbox/mbox-lock.c +++ b/src/lib-storage/index/mbox/mbox-lock.c @@ -120,6 +120,7 @@ static void mbox_read_lock_methods(const char *str, const char *env, static void mbox_init_lock_settings(void) { const char *str; + int r, w; str = getenv("MBOX_READ_LOCKS"); if (str == NULL) str = DEFAULT_READ_LOCK_METHODS; @@ -129,6 +130,21 @@ static void mbox_init_lock_settings(void) if (str == NULL) str = DEFAULT_WRITE_LOCK_METHODS; mbox_read_lock_methods(str, "MBOX_WRITE_LOCKS", write_locks); + /* check that read/write list orders match. write_locks must contain + at least read_locks and possibly more. */ + for (r = w = 0; write_locks[w] != (enum mbox_lock_type)-1; w++) { + if (read_locks[r] == (enum mbox_lock_type)-1) + break; + if (read_locks[r] == write_locks[w]) + r++; + } + if (read_locks[r] != (enum mbox_lock_type)-1) { + i_fatal("mbox read/write lock list settings are invalid. " + "Lock ordering must be the same with both, " + "and write locks must contain all read locks " + "(and possibly more)"); + } + str = getenv("MBOX_LOCK_TIMEOUT"); lock_timeout = str == NULL ? DEFAULT_LOCK_TIMEOUT : atoi(str); @@ -213,6 +229,9 @@ static int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type, return 1; } + if (ibox->mbox_dotlock.ino != 0) + return 1; + ctx->dotlock_last_stale = -1; ret = file_lock_dotlock(ibox->path, NULL, FALSE, lock_timeout, @@ -389,22 +408,11 @@ static int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type, return ret; } -int mbox_lock(struct index_mailbox *ibox, int lock_type, - unsigned int *lock_id_r) +static int mbox_update_locking(struct index_mailbox *ibox, int lock_type) { struct mbox_lock_context ctx; time_t max_wait_time; - int ret; - - /* allow only unlock -> shared/exclusive or exclusive -> shared */ - i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK); - i_assert(lock_type == F_RDLCK || ibox->mbox_lock_type != F_RDLCK); - - if (ibox->mbox_lock_type == lock_type) { - ibox->mbox_locks++; - *lock_id_r = ibox->mbox_lock_id; - return 1; - } + int ret, i, drop_locks; index_storage_lock_notify_reset(ibox); @@ -416,10 +424,23 @@ int mbox_lock(struct index_mailbox *ibox, int lock_type, memset(&ctx, 0, sizeof(ctx)); ctx.ibox = ibox; + if (ibox->mbox_lock_type == F_WRLCK) { + /* dropping to shared lock. first drop those that we + don't remove completely. */ + for (i = 0; i < MBOX_LOCK_COUNT; i++) + ctx.lock_status[i] = 1; + for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++) + ctx.lock_status[read_locks[i]] = 0; + drop_locks = TRUE; + } else { + drop_locks = FALSE; + } + ibox->mbox_lock_type = lock_type; - ret = mbox_lock_list(&ctx, ibox->mbox_lock_type, max_wait_time, 0); + ret = mbox_lock_list(&ctx, lock_type, max_wait_time, 0); if (ret <= 0) { - (void)mbox_unlock_files(&ctx); + if (!drop_locks) + (void)mbox_unlock_files(&ctx); if (ret == 0) { mail_storage_set_error(ibox->box.storage, "Timeout while waiting for lock"); @@ -427,8 +448,47 @@ int mbox_lock(struct index_mailbox *ibox, int lock_type, return ret; } - *lock_id_r = ++ibox->mbox_lock_id; - ibox->mbox_locks++; + if (drop_locks) { + /* dropping to shared lock: drop the locks that are only + in write list */ + memset(ctx.lock_status, 0, sizeof(ctx.lock_status)); + for (i = 0; write_locks[i] != (enum mbox_lock_type)-1; i++) + ctx.lock_status[write_locks[i]] = 1; + for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++) + ctx.lock_status[read_locks[i]] = 0; + + ibox->mbox_lock_type = F_WRLCK; + (void)mbox_lock_list(&ctx, F_UNLCK, 0, 0); + ibox->mbox_lock_type = F_RDLCK; + } + + return 1; +} + +int mbox_lock(struct index_mailbox *ibox, int lock_type, + unsigned int *lock_id_r) +{ + int ret; + + /* allow only unlock -> shared/exclusive or exclusive -> shared */ + i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK); + i_assert(lock_type == F_RDLCK || ibox->mbox_lock_type != F_RDLCK); + + if (ibox->mbox_lock_type == F_UNLCK) { + ret = mbox_update_locking(ibox, lock_type); + if (ret <= 0) + return ret; + + ibox->mbox_lock_id += 2; + } + + if (lock_type == F_RDLCK) { + ibox->mbox_shared_locks++; + *lock_id_r = ibox->mbox_lock_id; + } else { + ibox->mbox_excl_locks++; + *lock_id_r = ibox->mbox_lock_id + 1; + } return 1; } @@ -442,7 +502,7 @@ static int mbox_unlock_files(struct mbox_lock_context *ctx) /* make sure we don't keep mmap() between locks */ mbox_file_close_stream(ctx->ibox); - ctx->ibox->mbox_lock_id++; + ctx->ibox->mbox_lock_id += 2; ctx->ibox->mbox_lock_type = F_UNLCK; return ret; } @@ -452,11 +512,28 @@ int mbox_unlock(struct index_mailbox *ibox, unsigned int lock_id) struct mbox_lock_context ctx; int i; - i_assert(ibox->mbox_locks > 0); - i_assert(ibox->mbox_lock_id == lock_id); + i_assert(ibox->mbox_lock_id == (lock_id & ~1)); - if (--ibox->mbox_locks > 0) - return 0; + if (lock_id & 1) { + /* dropping exclusive lock */ + i_assert(ibox->mbox_excl_locks > 0); + if (--ibox->mbox_excl_locks > 0) + return 0; + if (ibox->mbox_shared_locks > 0) { + /* drop to shared lock */ + if (mbox_update_locking(ibox, F_RDLCK) < 0) + return -1; + return 0; + } + } else { + /* dropping shared lock */ + i_assert(ibox->mbox_shared_locks > 0); + if (--ibox->mbox_shared_locks > 0) + return 0; + if (ibox->mbox_excl_locks > 0) + return 0; + } + /* all locks gone */ memset(&ctx, 0, sizeof(ctx)); ctx.ibox = ibox; diff --git a/src/lib-storage/index/mbox/mbox-sync.c b/src/lib-storage/index/mbox/mbox-sync.c index a894aa4896..0ce7123c7b 100644 --- a/src/lib-storage/index/mbox/mbox-sync.c +++ b/src/lib-storage/index/mbox/mbox-sync.c @@ -1159,9 +1159,22 @@ int mbox_sync(struct index_mailbox *ibox, int last_commit, } } - if (sync_ctx.lock_id != 0 && (ret < 0 || !lock)) { - /* FIXME: drop to read locking and keep it MBOX_SYNC_SECS+1 - to make sure we notice changes made by others */ + if (sync_ctx.lock_id != 0 && ibox->mbox_lock_type != F_RDLCK) { + /* drop to read lock */ + unsigned int lock_id = 0; + + if (mbox_lock(ibox, F_RDLCK, &lock_id) <= 0) + ret = -1; + else { + if (mbox_unlock(ibox, sync_ctx.lock_id) < 0) + ret = -1; + sync_ctx.lock_id = lock_id; + } + } + + if (sync_ctx.lock_id != 0 && !lock) { + /* FIXME: keep the lock MBOX_SYNC_SECS+1 to make sure we + notice changes made by others */ if (mbox_unlock(ibox, sync_ctx.lock_id) < 0) ret = -1; }