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.