changeset 8929:9c50e7303513 HEAD

Added mail_index_atomic_inc_ext() for atomically incrementing numbers in extensions.
author Timo Sirainen <tss@iki.fi>
date Wed, 04 Mar 2009 17:40:24 -0500
parents 0fc03e326ccc
children cb37f2732abc
files src/lib-index/mail-index-sync-ext.c src/lib-index/mail-index-sync-private.h src/lib-index/mail-index-sync-update.c src/lib-index/mail-index-sync.c src/lib-index/mail-index-transaction-private.h src/lib-index/mail-index-transaction.c src/lib-index/mail-index.h src/lib-index/mail-transaction-log-append.c src/lib-index/mail-transaction-log.h src/util/logview.c
diffstat 10 files changed, 228 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-index/mail-index-sync-ext.c	Wed Mar 04 12:03:19 2009 -0500
+++ b/src/lib-index/mail-index-sync-ext.c	Wed Mar 04 17:40:24 2009 -0500
@@ -686,3 +686,63 @@
 	memcpy(old_data, u + 1, ext->record_size);
 	return 1;
 }
+
+int
+mail_index_sync_ext_atomic_inc(struct mail_index_sync_map_ctx *ctx,
+			       const struct mail_transaction_ext_atomic_inc *u)
+{
+	struct mail_index_view *view = ctx->view;
+	struct mail_index_record *rec;
+	const struct mail_index_ext *ext;
+	void *data;
+	uint32_t seq;
+
+	i_assert(ctx->cur_ext_map_idx != (uint32_t)-1);
+	i_assert(!ctx->cur_ext_ignore);
+
+	if (u->uid == 0 || u->uid >= view->map->hdr.next_uid) {
+		mail_index_sync_set_corrupted(ctx,
+			"Extension record inc for invalid uid=%u", u->uid);
+		return -1;
+	}
+
+	if (!mail_index_lookup_seq(view, u->uid, &seq))
+		return 1;
+
+	ext = array_idx(&view->map->extensions, ctx->cur_ext_map_idx);
+	i_assert(ext->record_offset + ext->record_size <=
+		 view->map->hdr.record_size);
+
+	rec = MAIL_INDEX_MAP_IDX(view->map, seq-1);
+	data = PTR_OFFSET(rec, ext->record_offset);
+
+	switch (ext->record_size) {
+	case 1: {
+		uint8_t *num = data;
+		*num += u->diff;
+		break;
+	}
+	case 2: {
+		uint16_t *num = data;
+		*num += u->diff;
+		break;
+	}
+	case 4: {
+		uint32_t *num = data;
+		*num += u->diff;
+		break;
+	}
+	case 8: {
+		uint64_t *num = data;
+		*num += u->diff;
+		break;
+	}
+	default:
+		mail_index_sync_set_corrupted(ctx,
+			"Extension record inc with invalid size=%u",
+			ext->record_size);
+		return -1;
+	}
+	mail_index_sync_write_seq_update(ctx, seq, seq);
+	return 1;
+}
--- a/src/lib-index/mail-index-sync-private.h	Wed Mar 04 12:03:19 2009 -0500
+++ b/src/lib-index/mail-index-sync-private.h	Wed Mar 04 17:40:24 2009 -0500
@@ -82,6 +82,9 @@
 int
 mail_index_sync_ext_rec_update(struct mail_index_sync_map_ctx *ctx,
 			       const struct mail_transaction_ext_rec_update *u);
+int
+mail_index_sync_ext_atomic_inc(struct mail_index_sync_map_ctx *ctx,
+			       const struct mail_transaction_ext_atomic_inc *u);
 
 int mail_index_sync_keywords(struct mail_index_sync_map_ctx *ctx,
 			     const struct mail_transaction_header *hdr,
--- a/src/lib-index/mail-index-sync-update.c	Wed Mar 04 12:03:19 2009 -0500
+++ b/src/lib-index/mail-index-sync-update.c	Wed Mar 04 17:40:24 2009 -0500
@@ -630,6 +630,31 @@
 		}
 		break;
 	}
+	case MAIL_TRANSACTION_EXT_ATOMIC_INC: {
+		const struct mail_transaction_ext_atomic_inc *rec, *end;
+
+		if (ctx->cur_ext_map_idx == (uint32_t)-1) {
+			mail_index_sync_set_corrupted(ctx,
+				"Extension record updated "
+				"without intro prefix");
+			ret = -1;
+			break;
+		}
+
+		if (ctx->cur_ext_ignore) {
+			ret = 1;
+			break;
+		}
+
+		rec = data;
+		end = CONST_PTR_OFFSET(data, hdr->size);
+		for (rec = data; rec < end; rec++) {
+			ret = mail_index_sync_ext_atomic_inc(ctx, rec);
+			if (ret <= 0)
+				break;
+		}
+		break;
+	}
 	case MAIL_TRANSACTION_KEYWORD_UPDATE: {
 		const struct mail_transaction_keyword_update *rec = data;
 
--- a/src/lib-index/mail-index-sync.c	Wed Mar 04 12:03:19 2009 -0500
+++ b/src/lib-index/mail-index-sync.c	Wed Mar 04 17:40:24 2009 -0500
@@ -504,6 +504,7 @@
 
 		switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
 		case MAIL_TRANSACTION_EXT_REC_UPDATE:
+		case MAIL_TRANSACTION_EXT_ATOMIC_INC:
 			/* extension record updates aren't exactly needed
 			   to be synced, but cache syncing relies on tail
 			   offsets being updated. */
--- a/src/lib-index/mail-index-transaction-private.h	Wed Mar 04 12:03:19 2009 -0500
+++ b/src/lib-index/mail-index-transaction-private.h	Wed Mar 04 17:40:24 2009 -0500
@@ -4,6 +4,8 @@
 #include "seq-range-array.h"
 #include "mail-transaction-log.h"
 
+ARRAY_DEFINE_TYPE(seq_array_array, ARRAY_TYPE(seq_array));
+
 struct mail_index_transaction_keyword_update {
 	ARRAY_TYPE(seq_range) add_seq;
 	ARRAY_TYPE(seq_range) remove_seq;
@@ -52,7 +54,8 @@
 
 	ARRAY_DEFINE(ext_hdr_updates,
 		     struct mail_index_transaction_ext_hdr_update);
-	ARRAY_DEFINE(ext_rec_updates, ARRAY_TYPE(seq_array));
+	ARRAY_TYPE(seq_array_array) ext_rec_updates;
+	ARRAY_TYPE(seq_array_array) ext_rec_atomics;
 	ARRAY_DEFINE(ext_resizes, struct mail_transaction_ext_intro);
 	ARRAY_DEFINE(ext_resets, struct mail_transaction_ext_reset);
 	ARRAY_DEFINE(ext_reset_ids, uint32_t);
--- a/src/lib-index/mail-index-transaction.c	Wed Mar 04 12:03:19 2009 -0500
+++ b/src/lib-index/mail-index-transaction.c	Wed Mar 04 17:40:24 2009 -0500
@@ -33,16 +33,22 @@
 
 	if (array_is_created(&t->ext_rec_updates)) {
 		recs = array_get_modifiable(&t->ext_rec_updates, &count);
-
 		for (i = 0; i < count; i++) {
 			if (array_is_created(&recs[i]))
 				array_free(&recs[i]);
 		}
 		array_free(&t->ext_rec_updates);
 	}
+	if (array_is_created(&t->ext_rec_atomics)) {
+		recs = array_get_modifiable(&t->ext_rec_atomics, &count);
+		for (i = 0; i < count; i++) {
+			if (array_is_created(&recs[i]))
+				array_free(&recs[i]);
+		}
+		array_free(&t->ext_rec_atomics);
+	}
 	if (array_is_created(&t->ext_hdr_updates)) {
 		ext_hdrs = array_get_modifiable(&t->ext_hdr_updates, &count);
-
 		for (i = 0; i < count; i++) {
 			i_free(ext_hdrs[i].data);
 			i_free(ext_hdrs[i].mask);
@@ -54,7 +60,6 @@
 		struct mail_index_transaction_keyword_update *u;
 
 		u = array_get_modifiable(&t->keyword_updates, &count);
-
 		for (i = 0; i < count; i++) {
 			if (array_is_created(&u[i].add_seq))
 				array_free(&u[i].add_seq);
@@ -313,6 +318,11 @@
 		for (i = 0; i < count; i++)
 			mail_index_convert_to_uids(t, (void *)&updates[i]);
 	}
+	if (array_is_created(&t->ext_rec_atomics)) {
+		updates = array_get_modifiable(&t->ext_rec_atomics, &count);
+		for (i = 0; i < count; i++)
+			mail_index_convert_to_uids(t, (void *)&updates[i]);
+	}
 
         keyword_updates_convert_to_uids(t);
 
@@ -382,6 +392,7 @@
 
 static void
 mail_index_transaction_sort_appends_ext(struct mail_index_transaction *t,
+					ARRAY_TYPE(seq_array_array) *updates,
 					const uint32_t *old_to_newseq_map)
 {
 	ARRAY_TYPE(seq_array) *ext_rec_arrays;
@@ -392,10 +403,10 @@
 	uint32_t seq;
 	unsigned int i, j, count;
 
-	if (!array_is_created(&t->ext_rec_updates))
+	if (!array_is_created(updates))
 		return;
 
-	ext_rec_arrays = array_get_modifiable(&t->ext_rec_updates, &count);
+	ext_rec_arrays = array_get_modifiable(updates, &count);
 	for (j = 0; j < count; j++) {
 		old_array = &ext_rec_arrays[j];
 		if (!array_is_created(old_array))
@@ -527,7 +538,10 @@
 		old_to_newseq_map[new_uid_map[i].idx] = i + t->first_new_seq;
 	i_free(new_uid_map);
 
-	mail_index_transaction_sort_appends_ext(t, old_to_newseq_map);
+	mail_index_transaction_sort_appends_ext(t, &t->ext_rec_updates,
+						old_to_newseq_map);
+	mail_index_transaction_sort_appends_ext(t, &t->ext_rec_atomics,
+						old_to_newseq_map);
 	mail_index_transaction_sort_appends_keywords(t, old_to_newseq_map);
 	i_free(old_to_newseq_map);
 
@@ -704,24 +718,37 @@
 }
 
 static void
-mail_index_expunge_last_append(struct mail_index_transaction *t, uint32_t seq)
+mail_index_expunge_last_append_ext(struct mail_index_transaction *t,
+				   ARRAY_TYPE(seq_array_array) *updates,
+				   uint32_t seq)
 {
 	ARRAY_TYPE(seq_array) *seqs;
+	unsigned int i, count, idx;
+
+	if (!array_is_created(updates))
+		return;
+
+	seqs = array_get_modifiable(&t->ext_rec_updates, &count);
+	for (i = 0; i < count; i++) {
+		if (array_is_created(&seqs[i]) &&
+		    mail_index_seq_array_lookup(&seqs[i], seq, &idx))
+			array_delete(&seqs[i], idx, 1);
+	}
+}
+
+static void
+mail_index_expunge_last_append(struct mail_index_transaction *t, uint32_t seq)
+{
 	struct mail_index_transaction_keyword_update *kw_updates;
-	unsigned int i, idx, count;
+	unsigned int i, count;
 
 	i_assert(seq == t->last_new_seq);
 
 	/* remove extension updates */
-	if (array_is_created(&t->ext_rec_updates)) {
-		seqs = array_get_modifiable(&t->ext_rec_updates, &count);
-		for (i = 0; i < count; i++) {
-			if (array_is_created(&seqs[i]) &&
-			    mail_index_seq_array_lookup(&seqs[i], seq, &idx))
-				array_delete(&seqs[i], idx, 1);
-		}
-		t->log_ext_updates = mail_index_transaction_has_ext_changes(t);
-	}
+	mail_index_expunge_last_append_ext(t, &t->ext_rec_updates, seq);
+	mail_index_expunge_last_append_ext(t, &t->ext_rec_atomics, seq);
+	t->log_ext_updates = mail_index_transaction_has_ext_changes(t);
+
 	/* remove keywords */
 	if (array_is_created(&t->keyword_resets))
 		seq_range_array_remove(&t->keyword_resets, seq);
@@ -1146,23 +1173,35 @@
 }
 
 static bool
-mail_index_transaction_has_ext_changes(struct mail_index_transaction *t)
+mail_index_transaction_has_ext_updates(const ARRAY_TYPE(seq_array_array) *arr)
 {
+	const ARRAY_TYPE(seq_array) *array;
 	unsigned int i, count;
 
-	if (array_is_created(&t->ext_rec_updates)) {
-		const ARRAY_TYPE(seq_array) *array;
-
-		array = array_get(&t->ext_rec_updates, &count);
+	if (array_is_created(arr)) {
+		array = array_get(arr, &count);
 		for (i = 0; i < count; i++) {
 			if (array_is_created(&array[i]))
 				return TRUE;
 		}
 	}
+	return FALSE;
+}
+
+static bool
+mail_index_transaction_has_ext_changes(struct mail_index_transaction *t)
+{
+	unsigned int i, count;
+
+	if (mail_index_transaction_has_ext_updates(&t->ext_rec_updates))
+		return TRUE;
+	if (mail_index_transaction_has_ext_updates(&t->ext_rec_atomics))
+		return TRUE;
+
 	if (array_is_created(&t->ext_hdr_updates)) {
 		const struct mail_index_transaction_ext_hdr_update *hdr;
 
-		hdr = array_get(&t->ext_hdr_updates, &count);
+		hdr = array_get(&t->ext_hdr_updates, &count);
 		for (i = 0; i < count; i++) {
 			if (hdr[i].alloc_size > 0)
 				return TRUE;
@@ -1189,18 +1228,24 @@
 	return FALSE;
 }
 
+static void
+mail_index_ext_update_reset(ARRAY_TYPE(seq_array_array) *arr, uint32_t ext_id)
+{
+	if (array_is_created(arr) && ext_id < array_count(arr)) {
+		/* if extension records have been updated, clear them */
+		ARRAY_TYPE(seq_array) *array;
+
+		array = array_idx_modifiable(arr, ext_id);
+		if (array_is_created(array))
+			array_clear(array);
+	}
+}
+
 void mail_index_ext_set_reset_id(struct mail_index_transaction *t,
 				 uint32_t ext_id, uint32_t reset_id)
 {
-	if (array_is_created(&t->ext_rec_updates) &&
-	    ext_id < array_count(&t->ext_rec_updates)) {
-		/* if extension records have been updated, clear them */
-		ARRAY_TYPE(seq_array) *array;
-
-		array = array_idx_modifiable(&t->ext_rec_updates, ext_id);
-		if (array_is_created(array))
-			array_clear(array);
-	}
+	mail_index_ext_update_reset(&t->ext_rec_updates, ext_id);
+	mail_index_ext_update_reset(&t->ext_rec_atomics, ext_id);
 	if (array_is_created(&t->ext_hdr_updates) &&
 	    ext_id < array_count(&t->ext_hdr_updates)) {
 		/* if extension headers have been updated, clear them */
@@ -1302,6 +1347,24 @@
 	}
 }
 
+void mail_index_atomic_inc_ext(struct mail_index_transaction *t, uint32_t seq,
+			       uint32_t ext_id, int diff)
+{
+	ARRAY_TYPE(seq_array) *array;
+	int32_t diff32 = diff;
+
+	i_assert(seq > 0 &&
+		 (seq <= mail_index_view_get_messages_count(t->view) ||
+		  seq <= t->last_new_seq));
+	i_assert(ext_id < array_count(&t->view->index->extensions));
+
+	t->log_ext_updates = TRUE;
+	if (!array_is_created(&t->ext_rec_atomics))
+		i_array_init(&t->ext_rec_atomics, ext_id + 2);
+	array = array_idx_modifiable(&t->ext_rec_atomics, ext_id);
+	mail_index_seq_array_add(array, seq, &diff32, sizeof(diff32), NULL);
+}
+
 struct mail_keywords *
 mail_index_keywords_create(struct mail_index *index,
 			   const char *const keywords[])
--- a/src/lib-index/mail-index.h	Wed Mar 04 12:03:19 2009 -0500
+++ b/src/lib-index/mail-index.h	Wed Mar 04 17:40:24 2009 -0500
@@ -498,5 +498,8 @@
    now overwriting. */
 void mail_index_update_ext(struct mail_index_transaction *t, uint32_t seq,
 			   uint32_t ext_id, const void *data, void *old_data);
+/* Increase/decrease number in extension atomically. */
+void mail_index_atomic_inc_ext(struct mail_index_transaction *t, uint32_t seq,
+			       uint32_t ext_id, int diff);
 
 #endif
--- a/src/lib-index/mail-transaction-log-append.c	Wed Mar 04 12:03:19 2009 -0500
+++ b/src/lib-index/mail-transaction-log-append.c	Wed Mar 04 17:40:24 2009 -0500
@@ -428,21 +428,16 @@
 	}
 }
 
-static void log_append_ext_rec_updates(struct log_append_context *ctx)
+static void log_append_ext_recs(struct log_append_context *ctx,
+				const ARRAY_TYPE(seq_array_array) *arr,
+				enum mail_transaction_type type)
 {
 	struct mail_index_transaction *t = ctx->trans;
-	ARRAY_TYPE(seq_array) *updates;
+	const ARRAY_TYPE(seq_array) *updates;
 	const uint32_t *reset_ids;
 	unsigned int ext_id, count, reset_id_count;
 	uint32_t reset_id;
 
-	if (!array_is_created(&t->ext_rec_updates)) {
-		updates = NULL;
-		count = 0;
-	} else {
-		updates = array_get_modifiable(&t->ext_rec_updates, &count);
-	}
-
 	if (!array_is_created(&t->ext_reset_ids)) {
 		reset_ids = NULL;
 		reset_id_count = 0;
@@ -451,6 +446,7 @@
 						 &reset_id_count);
 	}
 
+	updates = array_get(arr, &count);
 	for (ext_id = 0; ext_id < count; ext_id++) {
 		if (!array_is_created(&updates[ext_id]))
 			continue;
@@ -458,8 +454,7 @@
 		reset_id = ext_id < reset_id_count ? reset_ids[ext_id] : 0;
 		log_append_ext_intro(ctx, ext_id, reset_id);
 
-		log_append_buffer(ctx, updates[ext_id].arr.buffer, NULL,
-				  MAIL_TRANSACTION_EXT_REC_UPDATE);
+		log_append_buffer(ctx, updates[ext_id].arr.buffer, NULL, type);
 	}
 }
 
@@ -637,8 +632,14 @@
 				  MAIL_TRANSACTION_FLAG_UPDATE);
 	}
 
-	if (array_is_created(&t->ext_rec_updates))
-		log_append_ext_rec_updates(&ctx);
+	if (array_is_created(&t->ext_rec_updates)) {
+		log_append_ext_recs(&ctx, &t->ext_rec_updates,
+				    MAIL_TRANSACTION_EXT_REC_UPDATE);
+	}
+	if (array_is_created(&t->ext_rec_atomics)) {
+		log_append_ext_recs(&ctx, &t->ext_rec_atomics,
+				    MAIL_TRANSACTION_EXT_ATOMIC_INC);
+	}
 
 	/* keyword resets before updates */
 	if (array_is_created(&t->keyword_resets)) {
--- a/src/lib-index/mail-transaction-log.h	Wed Mar 04 12:03:19 2009 -0500
+++ b/src/lib-index/mail-transaction-log.h	Wed Mar 04 17:40:24 2009 -0500
@@ -36,12 +36,14 @@
 	MAIL_TRANSACTION_EXT_REC_UPDATE		= 0x00000200,
 	MAIL_TRANSACTION_KEYWORD_UPDATE		= 0x00000400,
 	MAIL_TRANSACTION_KEYWORD_RESET		= 0x00000800,
+	MAIL_TRANSACTION_EXT_ATOMIC_INC		= 0x00001000,
 
 	MAIL_TRANSACTION_TYPE_MASK		= 0x0000ffff,
 
 #define MAIL_TRANSACTION_EXT_MASK \
 	(MAIL_TRANSACTION_EXT_INTRO | MAIL_TRANSACTION_EXT_RESET | \
-	MAIL_TRANSACTION_EXT_HDR_UPDATE | MAIL_TRANSACTION_EXT_REC_UPDATE)
+	MAIL_TRANSACTION_EXT_HDR_UPDATE | MAIL_TRANSACTION_EXT_REC_UPDATE | \
+	MAIL_TRANSACTION_EXT_ATOMIC_INC)
 
 	/* since we'll expunge mails based on data read from transaction log,
 	   try to avoid the possibility of corrupted transaction log expunging
@@ -124,6 +126,10 @@
 	uint32_t uid;
 	/* unsigned char data[]; */
 };
+struct mail_transaction_ext_atomic_inc {
+	uint32_t uid;
+	int32_t diff;
+};
 
 #define LOG_IS_BEFORE(seq1, offset1, seq2, offset2) \
 	(((offset1) < (offset2) && (seq1) == (seq2)) || (seq1) < (seq2))
--- a/src/util/logview.c	Wed Mar 04 12:03:19 2009 -0500
+++ b/src/util/logview.c	Wed Mar 04 17:40:24 2009 -0500
@@ -102,6 +102,9 @@
 	case MAIL_TRANSACTION_KEYWORD_RESET:
 		name = "keyword-reset";
 		break;
+	case MAIL_TRANSACTION_EXT_ATOMIC_INC:
+		name = "ext-atomic-inc";
+		break;
 	default:
 		name = t_strdup_printf("unknown: %x", type);
 		break;
@@ -323,6 +326,19 @@
 		}
 		break;
 	}
+	case MAIL_TRANSACTION_EXT_ATOMIC_INC: {
+		const struct mail_transaction_ext_atomic_inc *rec = data, *end;
+
+		end = CONST_PTR_OFFSET(data, size);
+		for (; rec < end; rec++) {
+			printf(" - %u: ", rec->uid);
+			if (rec->diff > 0)
+				printf("+%d\n", rec->diff);
+			else
+				printf("%d\n", rec->diff);
+		}
+		break;
+	}
 	case MAIL_TRANSACTION_KEYWORD_UPDATE: {
 		const struct mail_transaction_keyword_update *u = data;
 		const uint32_t *uid;