Fix deadlock problem reported by John D. Heintz.
If one thread was committing a transaction and another thread was opening a new DB connection, deadlock could occur. The cause of the deadlock is that tpc_finish() acquires the storage and db locks in a different order than DB.open(). As a result, if each starts at the same time and gets one of the two locks it needs, the system will be deadlocked. The solution is to enforce a consistent locking order. If a thread is going to hold the DB lock and the storage lock, it MUST acquire the DB lock first. This patch implements that locking order for the invalidation in tpc_finish(). The DB object gets methods called begin_invalidation() and finish_invalidation() that acquire and release the DB lock respectively. Before the Connection calls tpc_finish() on the storage, it calls begin_invalidation(). This guarantees that the DB acquired before the storage lock. When the invalidation phase is over, the Connection calls end_invalidation() to release the DB lock. This is an optimization. It could wait until tpc_finish() returns, but we know that the DB will not be used again for the rest of the tpc_finish() and tpc_finish() could take a long time. Specific changes: DB.py begin_invalidation(): Added. finish_invalidation(): Added. invalidate(): Remove locking. invalidateMany(): Add comment about how it should be used. Connection.py tpc_finish(): Don't pass second argument to storage's tpc_finish() when committing a transaction with no data. Add call to begin_invalidation() before calling storage's tpc_finish(). _invalidate_sub(): Remove empty, unnecessary method. _invalidating_invalidating(): Add call to finish_invalidation() after last call to DB's invalidate().
Showing
Please register or sign in to comment