]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Allow dropping exclusive mbox locks to shared locks.
authorTimo Sirainen <tss@iki.fi>
Sun, 20 Jun 2004 11:17:53 +0000 (14:17 +0300)
committerTimo Sirainen <tss@iki.fi>
Sun, 20 Jun 2004 11:17:53 +0000 (14:17 +0300)
--HG--
branch : HEAD

src/lib-storage/index/index-storage.h
src/lib-storage/index/mbox/mbox-lock.c
src/lib-storage/index/mbox/mbox-sync.c

index 82cf4f59d84a8491a629f6f95e91093dfcc232d7..f4e803aff6cd06f26a64bb030d557245769da463 100644 (file)
@@ -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;
index 2b9fd4a00bba6180b7fc0b019f544e2785abdbd9..b899e3b42efe026ffc6e2943febbbb67c4053edd 100644 (file)
@@ -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;
index a894aa48962fff85ed59852098ef36ec1df5d0ac..0ce7123c7b73afe7c260167a47e92e61cdd3fd3c 100644 (file)
@@ -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;
        }