Looking at it, the check for closed
in CursorBackend::assert_txn_backend()
appears entirely redundant. To call assert_txn_backend()
, you must have a valid &CursorBackend
and a valid &TxnBackend
. The only operations which close a cursor are <CursorBackend as Drop>::drop()
and TxnBackend::close_cursors()
, which respectively require a &mut CursorBackend
or &mut TxnBackend
. Since both of those are part of a destructor, and neither calls assert_txn_backend()
, all possible callers of assert_txn_backend()
must ensure that the end of each &Backend
's lifetime happens-before the destruction of each Backend
. (At least, Miri enforces that references' liveness durations must follow the happens-before relationship, even though I can't find it explicitly documented anywhere.) Therefore, the cursor can never be closed when assert_txn_backend()
is called.
All that's left is to ensure that CursorBackend::drop()
and TxnBackend::close_cursors()
don't both call lmdb::mdb_cursor_close()
. Dropping the upgraded Arc<CursorBackend>
within close_cursors()
synchronizes-with dropping the Arc<CursorBackend>
in a Cursor<K, V, C>
, since Arc
uses an release–acquire pair on its reference count before calling the inner value's destructor. (If it didn't, it would be unsound, as before.) Therefore, the mdb_cursor_close()
in TxnBackend::close_cursors()
on one thread must always happen-before CursorBackend::drop()
is called on another thread. Consequently, no additional synchronization is needed on closed
: it could use Relaxed
operations, or even an UnsafeCell
, since Arc
handles the necessary synchronization for us. (In either case, CursorBackend::drop()
can just safely use get_mut()
.)