[Zodb-checkins] SVN: ZODB/trunk/src/ZODB/DB.py Foward port fix for
race in DB.open():
Tim Peters
tim.one at comcast.net
Fri May 21 20:55:01 EDT 2004
Log message for revision 24866:
Foward port fix for race in DB.open():
Under exceedingly rare conditions, a timing hole made it
possible for a second open() call on a database to block for an
arbitrarily long time. This accounts for the intermittent
failure of a thread to make any progress after 5 minutes in
checkConcurrentUpdates1Storage.
We intend to get rid of most of this delicate lock
business, but before then the test failures are still
hurting me.
-=-
Modified: ZODB/trunk/src/ZODB/DB.py
===================================================================
--- ZODB/trunk/src/ZODB/DB.py 2004-05-21 21:22:20 UTC (rev 24865)
+++ ZODB/trunk/src/ZODB/DB.py 2004-05-22 00:55:00 UTC (rev 24866)
@@ -487,9 +487,9 @@
# set whenever the pool becomes empty so that threads are
# forced to wait until the pool gets a connection in it.
# The lock is acquired when the (empty) pool is
- # created. The The lock is acquired just prior to removing
- # the last connection from the pool and just after adding
- # a connection to an empty pool.
+ # created. The lock is acquired just prior to removing
+ # the last connection from the pool and released just after
+ # adding a connection to an empty pool.
if pools.has_key(version):
@@ -528,22 +528,36 @@
pool_lock.release()
else: return
- elif len(pool) == 1:
- # Taking last one, lock the pool
+ elif len(pool)==1:
+ # Taking last one, lock the pool.
# Note that another thread might grab the lock
# before us, so we might actually block, however,
# when we get the lock back, there *will* be a
- # connection in the pool.
+ # connection in the pool. OTOH, there's no limit on
+ # how long we may need to wait: if the other thread
+ # grabbed the lock in this section too, we'll wait
+ # here until another connection is closed.
+ # checkConcurrentUpdates1Storage provoked this frequently
+ # on a hyperthreaded machine, with its second thread
+ # timing out after waiting 5 minutes for DB.open() to
+ # return. So, if we can't get the pool lock immediately,
+ # now we make a recursive call. This allows the current
+ # thread to allocate a new connection instead of waiting
+ # arbitrarily long for the single connection in the pool
+ # right now.
self._r()
- pool_lock.acquire()
+ if not pool_lock.acquire(0):
+ result = DB.open(self, version, transaction, temporary,
+ force, waitflag)
+ self._a()
+ return result
self._a()
if len(pool) > 1:
# Note that the pool size will normally be 1 here,
# but it could be higher due to a race condition.
pool_lock.release()
- c = pool[-1]
- del pool[-1]
+ c = pool.pop()
c._setDB(self, mvcc=mvcc, txn_mgr=txn_mgr, synch=synch)
for pool, allocated in pooll:
for cc in pool:
@@ -553,7 +567,8 @@
transaction[version] = c
return c
- finally: self._r()
+ finally:
+ self._r()
def removeVersionPool(self, version):
pools, pooll = self._pools
More information about the Zodb-checkins
mailing list