Believe it or not, I'm actually not perfect, and there's a doozy of a bug in skiplists that I'm probably entirely responsible for.
If your foreach callback function returns a non-zero result, then it double-unlocks. Wow, what a pain. Attached patch fixes it. I've run it on my testbed happily, and will roll it out to production tomorrow. Bron ( releases by embarassment - I think it's getting on time for a 2.3.15 if we can round up all our bugfixes )
diff --git a/lib/cyrusdb_skiplist.c b/lib/cyrusdb_skiplist.c index 36110ca..5b481d4 100644 --- a/lib/cyrusdb_skiplist.c +++ b/lib/cyrusdb_skiplist.c @@ -1070,6 +1070,7 @@ int myforeach(struct db *db, size_t savebuflen = 0; size_t savebufsize; int r = 0, cb_r = 0; + int need_unlock = 0; assert(db != NULL); assert(prefixlen >= 0); @@ -1093,6 +1094,7 @@ int myforeach(struct db *db, if ((r = read_lock(db)) < 0) { return r; } + need_unlock = 1; } ptr = find_node(db, prefix, prefixlen, 0); @@ -1112,6 +1114,7 @@ int myforeach(struct db *db, if ((r = unlock(db)) < 0) { return r; } + need_unlock = 0; } /* save KEY, KEYLEN */ @@ -1131,6 +1134,7 @@ int myforeach(struct db *db, if ((r = read_lock(db)) < 0) { return r; } + need_unlock = 1; } else { /* make sure we're up to date */ update_lock(db, *tidptr); @@ -1161,7 +1165,7 @@ int myforeach(struct db *db, } } - if (!tidptr) { + if (need_unlock) { /* release read lock */ if ((r = unlock(db)) < 0) { return r;