[Zope-Checkins] CVS: ZODB3/ZEO - ClientStorage.py:1.73.2.17
Tim Peters
tim.one@comcast.net
Thu, 5 Jun 2003 16:33:37 -0400
Update of /cvs-repository/ZODB3/ZEO
In directory cvs.zope.org:/tmp/cvs-serv27626/ZEO
Modified Files:
Tag: ZODB3-3_1-branch
ClientStorage.py
Log Message:
load() and tpc_finish(): The new locking strategy was vulnerable to
deadlock, when e.g. an invalidation came in *during* a zeoLoad call.
The locks now only seek to make [updating the cache] mutually exclusive
with [receiving invalidations].
=== ZODB3/ZEO/ClientStorage.py 1.73.2.16 => 1.73.2.17 ===
--- ZODB3/ZEO/ClientStorage.py:1.73.2.16 Thu Jun 5 15:45:07 2003
+++ ZODB3/ZEO/ClientStorage.py Thu Jun 5 16:33:37 2003
@@ -219,7 +219,7 @@
# but _server is set only after cache verification has finished
# and clients can safely use the server. _pending_server holds
# a server stub while it is being verified.
-
+
self._server = disconnected_stub
self._connection = None
self._pending_server = None
@@ -363,7 +363,7 @@
# If there is no connection, return immediately. Technically,
# there are no pending invalidations so they are all handled.
# There doesn't seem to be much benefit to raising an exception.
-
+
cn = self._connection
if cn is not None:
cn.pending()
@@ -608,25 +608,29 @@
specified by the given object id and version, if they exist;
otherwise a KeyError is raised.
"""
- self._lock.acquire()
+ self._lock.acquire() # for atomic processing of invalidations
try:
p = self._cache.load(oid, version)
if p:
return p
- if self._server is None:
- raise ClientDisconnected()
- p, s, v, pv, sv = self._server.zeoLoad(oid)
- self._cache.checkSize(0)
- self._cache.store(oid, p, s, v, pv, sv)
- if v and version and v == version:
- return pv, sv
- else:
- if s:
- return p, s
- raise KeyError, oid # no non-version data for this
finally:
self._lock.release()
+ if self._server is None:
+ raise ClientDisconnected()
+
+ # If an invalidation for oid comes in during zeoLoad, that's OK
+ # because we'll get oid's new state.
+ p, s, v, pv, sv = self._server.zeoLoad(oid)
+ self._cache.checkSize(0)
+ self._cache.store(oid, p, s, v, pv, sv)
+ if v and version and v == version:
+ return pv, sv
+ else:
+ if s:
+ return p, s
+ raise KeyError, oid # no non-version data for this
+
def modifiedInVersion(self, oid):
"""Storage API: return the version, if any, that modfied an object.
@@ -778,19 +782,20 @@
if transaction is not self._transaction:
return
try:
- self._lock.acquire()
+ self._lock.acquire() # for atomic processing of invalidations
try:
- if f is not None:
- f()
-
- self._server.tpc_finish(self._serial)
-
- r = self._check_serials()
- assert r is None or len(r) == 0, "unhandled serialnos: %s" % r
-
self._update_cache()
finally:
self._lock.release()
+
+ if f is not None:
+ f()
+
+ self._server.tpc_finish(self._serial)
+
+ r = self._check_serials()
+ assert r is None or len(r) == 0, "unhandled serialnos: %s" % r
+
finally:
self.end_transaction()
@@ -801,7 +806,7 @@
update or invalidate the cache.
"""
# Must be called with _lock already acquired.
-
+
self._cache.checkSize(self._tbuf.get_size())
try:
self._tbuf.begin_iterate()