Description
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.