There's a race in the btree close code:
if (F_ISSET(btree, WT_BTREE_OPEN))
WT_STAT_DECR(conn->stats, file_open);
/* Remove from the connection's list. */
__wt_spin_lock(session, &conn->spinlock);
inuse = (--btree->refcnt > 0);
if (!inuse) {
TAILQ_REMOVE(&conn->btqh, btree, q);
--conn->btqcnt;
}
__wt_spin_unlock(session, &conn->spinlock);
if (inuse)
return (0);
ret = __wt_btree_close(session);
__wt_free(session, btree->name);
__wt_free(session, btree->filename);
__wt_free(session, btree->config);
Here's the sequence:
Thread WT-1: acquire conn->spinlock find btree, refcnt == 0 remove btree from queue release conn->spinlock Thread WT-2: acquire conn->spinlock don't find btree entry add btree to queue acquire btree->rwlock release conn->spinlock open btree (reading the underlying root page and free list) release btree->rwlock Thread WT-1: call __wt_btree_close (writing dirty pages and the underlying free list)
and now we've got inconsistent views of the file.