• Jeremy Hylton's avatar
    Fix deadlock problem reported by John D. Heintz. · 30e6b67a
    Jeremy Hylton authored
    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().
    30e6b67a
Connection.py 24.7 KB