While a number of errors can happen when iterating over threads (DatabaseModifiedError, memory allocation errors, etc.), the API currently cannot signal them to the caller, and either triggers an internal error (aborting the caller) or returns NULL from notmuch_threads_get() with no information on what actually went wrong.
Add a new public function notmuch_threads_status() - similar to previously added notmuch_messages_status() - that allows propagating those errors to the caller. Use this to remove the INTERNAL_ERROR() in _notmuch_thread_create() (triggered by T642). Fixes: https://github.com/pazz/alot/issues/1460 --- Applied the review comments --- lib/notmuch-private.h | 5 ++-- lib/notmuch.h | 31 ++++++++++++++++++--- lib/query.cc | 37 +++++++++++++++++++------- lib/thread.cc | 24 ++++++++++------- test/T642-database-modified-threads.sh | 5 ++-- 5 files changed, 76 insertions(+), 26 deletions(-) diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index db49e77d..89ab5397 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -707,14 +707,15 @@ _notmuch_filenames_create (const void *ctx, /* thread.cc */ -notmuch_thread_t * +notmuch_private_status_t _notmuch_thread_create (void *ctx, notmuch_database_t *notmuch, unsigned int seed_doc_id, notmuch_doc_id_set_t *match_set, notmuch_string_list_t *excluded_terms, notmuch_exclude_t omit_exclude, - notmuch_sort_t sort); + notmuch_sort_t sort, + notmuch_thread_t **thread); /* indexopts.c */ diff --git a/lib/notmuch.h b/lib/notmuch.h index e634e41f..95918fc2 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -1134,7 +1134,7 @@ notmuch_query_add_tag_exclude (notmuch_query_t *query, const char *tag); * * for (stat = notmuch_query_search_threads (query, &threads); * stat == NOTMUCH_STATUS_SUCCESS && - * notmuch_threads_valid (threads); + * ! notmuch_threads_status (threads); * notmuch_threads_move_to_next (threads)) * { * thread = notmuch_threads_get (threads); @@ -1259,10 +1259,35 @@ notmuch_query_destroy (notmuch_query_t *query); * * See the documentation of notmuch_query_search_threads for example * code showing how to iterate over a notmuch_threads_t object. + * + * Note that an iterator may become invalid either due to getting exhausted or + * due to a runtime error. Use notmuch_threads_status to distinguish + * between those cases. */ notmuch_bool_t notmuch_threads_valid (notmuch_threads_t *threads); +/** + * Get the status of the given 'threads' iterator. + * + * Return value: + * + * @retval #NOTMUCH_STATUS_SUCCESS The iterator is valid; notmuch_threads_get + * may return a valid object + * + * @retval #NOTMUCH_STATUS_ITERATOR_EXHAUSTED All items have been read + * + * @retval #NOTMUCH_STATUS_OUT_OF_MEMORY Iteration failed to allocate memory + * + * @retval #NOTMUCH_STATUS_OPERATION_INVALIDATED Iteration was invalidated by + * the database. Re-open the database and try again. + * + * See the documentation of notmuch_query_search_threads for example + * code showing how to iterate over a notmuch_threads_t object. + */ +notmuch_status_t +notmuch_threads_status (notmuch_threads_t *threads); + /** * Get the current thread from 'threads' as a notmuch_thread_t. * @@ -1283,8 +1308,8 @@ notmuch_threads_get (notmuch_threads_t *threads); * * If 'threads' is already pointing at the last thread then the * iterator will be moved to a point just beyond that last thread, - * (where notmuch_threads_valid will return FALSE and - * notmuch_threads_get will return NULL). + * (where notmuch_threads_status will return NOTMUCH_STATUS_ITERATOR_EXHAUSTED + * and notmuch_threads_get will return NULL). * * See the documentation of notmuch_query_search_threads for example * code showing how to iterate over a notmuch_threads_t object. diff --git a/lib/query.cc b/lib/query.cc index 976fe76d..7c4fef4b 100644 --- a/lib/query.cc +++ b/lib/query.cc @@ -61,6 +61,7 @@ struct _notmuch_threads { /* The set of matched docid's that have not been assigned to a * thread. Initially, this contains every docid in doc_ids. */ notmuch_doc_id_set_t match_set; + notmuch_status_t status; }; /* We need this in the message functions so forward declare. */ @@ -611,6 +612,7 @@ notmuch_query_search_threads (notmuch_query_t *query, threads = talloc (query, notmuch_threads_t); if (threads == NULL) return NOTMUCH_STATUS_OUT_OF_MEMORY; + threads->status = NOTMUCH_STATUS_SUCCESS; threads->doc_ids = NULL; talloc_set_destructor (threads, _notmuch_threads_destructor); @@ -650,11 +652,19 @@ notmuch_query_destroy (notmuch_query_t *query) notmuch_bool_t notmuch_threads_valid (notmuch_threads_t *threads) +{ + return notmuch_threads_status (threads) == NOTMUCH_STATUS_SUCCESS; +} + +notmuch_status_t +notmuch_threads_status (notmuch_threads_t *threads) { unsigned int doc_id; if (! threads) - return false; + return NOTMUCH_STATUS_ITERATOR_EXHAUSTED; + if (threads->status) + return threads->status; while (threads->doc_id_pos < threads->doc_ids->len) { doc_id = g_array_index (threads->doc_ids, unsigned int, @@ -665,26 +675,35 @@ notmuch_threads_valid (notmuch_threads_t *threads) threads->doc_id_pos++; } - return threads->doc_id_pos < threads->doc_ids->len; + return (threads->doc_id_pos < threads->doc_ids->len) ? + NOTMUCH_STATUS_SUCCESS : NOTMUCH_STATUS_ITERATOR_EXHAUSTED; } notmuch_thread_t * notmuch_threads_get (notmuch_threads_t *threads) { + notmuch_thread_t *thread; unsigned int doc_id; + notmuch_private_status_t status; if (! notmuch_threads_valid (threads)) return NULL; doc_id = g_array_index (threads->doc_ids, unsigned int, threads->doc_id_pos); - return _notmuch_thread_create (threads->query, - threads->query->notmuch, - doc_id, - &threads->match_set, - threads->query->exclude_terms, - threads->query->omit_excluded, - threads->query->sort); + status = _notmuch_thread_create (threads->query, + threads->query->notmuch, + doc_id, + &threads->match_set, + threads->query->exclude_terms, + threads->query->omit_excluded, + threads->query->sort, + &thread); + if (status) { + threads->status = COERCE_STATUS (status, "error creating a thread"); + } + + return thread; } void diff --git a/lib/thread.cc b/lib/thread.cc index 168a9e8b..75b0f188 100644 --- a/lib/thread.cc +++ b/lib/thread.cc @@ -544,16 +544,17 @@ _resolve_thread_relationships (notmuch_thread_t *thread) * * Here, 'ctx' is talloc context for the resulting thread object. * - * This function returns NULL in the case of any error. + * This function write NULL in the case of any error. */ -notmuch_thread_t * +notmuch_private_status_t _notmuch_thread_create (void *ctx, notmuch_database_t *notmuch, unsigned int seed_doc_id, notmuch_doc_id_set_t *match_set, notmuch_string_list_t *exclude_terms, notmuch_exclude_t omit_excluded, - notmuch_sort_t sort) + notmuch_sort_t sort, + notmuch_thread_t **pthread) { void *local = talloc_new (ctx); notmuch_thread_t *thread = NULL; @@ -564,11 +565,13 @@ _notmuch_thread_create (void *ctx, notmuch_messages_t *messages; notmuch_message_t *message; - notmuch_status_t status; + notmuch_private_status_t status; - seed_message = _notmuch_message_create (local, notmuch, seed_doc_id, NULL); - if (! seed_message) - INTERNAL_ERROR ("Thread seed message %u does not exist", seed_doc_id); + *pthread = NULL; + + seed_message = _notmuch_message_create (local, notmuch, seed_doc_id, &status); + if (status) + return status; thread_id = notmuch_message_get_thread_id (seed_message); thread_id_query_string = talloc_asprintf (local, "thread:%s", thread_id); @@ -623,7 +626,7 @@ _notmuch_thread_create (void *ctx, * oldest or newest subject is desired. */ notmuch_query_set_sort (thread_id_query, NOTMUCH_SORT_OLDEST_FIRST); - status = notmuch_query_search_messages (thread_id_query, &messages); + status = (notmuch_private_status_t) notmuch_query_search_messages (thread_id_query, &messages); if (status) goto DONE; @@ -656,10 +659,13 @@ _notmuch_thread_create (void *ctx, /* Commit to returning thread. */ (void) talloc_steal (ctx, thread); + *pthread = thread; DONE: talloc_free (local); - return thread; + if (! *pthread && ! status) + status = NOTMUCH_PRIVATE_STATUS_OUT_OF_MEMORY; + return status; } notmuch_messages_t * diff --git a/test/T642-database-modified-threads.sh b/test/T642-database-modified-threads.sh index 00bd7808..cc36a4c4 100755 --- a/test/T642-database-modified-threads.sh +++ b/test/T642-database-modified-threads.sh @@ -11,7 +11,6 @@ add_email_corpus cp -a $NOTMUCH_SRCDIR/test/corpora/lkml ${MAIL_DIR}/ test_begin_subtest "handling NOTMUCH_STATUS_OPERATION_INVALIDATED in _notmuch_thread_create" -test_subtest_known_broken test_C ${MAIL_DIR} <<EOF #include <notmuch-test.h> @@ -65,11 +64,11 @@ main (int argc, char **argv) // on the first try for (try = 0; try < 2; try++) { for (; - notmuch_threads_valid (threads); + ! notmuch_threads_status (threads); notmuch_threads_move_to_next (threads)) { notmuch_thread_t *thread = notmuch_threads_get (threads); } - status = NOTMUCH_STATUS_ITERATOR_EXHAUSTED; + status = notmuch_threads_status (threads); if (status != NOTMUCH_STATUS_OPERATION_INVALIDATED) break; -- 2.39.5 _______________________________________________ notmuch mailing list -- notmuch@notmuchmail.org To unsubscribe send an email to notmuch-le...@notmuchmail.org