Commit ab50500f authored by Tim Peters's avatar Tim Peters

tpc_begin(): self._transaction wasn't protected by the condvar at all

anymore.  Tried to fix that.  Jeremy, please review the new XXX comments.
parent 4e3ab3b2
...@@ -2,18 +2,18 @@ ...@@ -2,18 +2,18 @@
# #
# Copyright (c) 2001, 2002 Zope Corporation and Contributors. # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved. # All Rights Reserved.
# #
# This software is subject to the provisions of the Zope Public License, # This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE
# #
############################################################################## ##############################################################################
"""Network ZODB storage client """Network ZODB storage client
$Id: ClientStorage.py,v 1.46 2002/08/16 18:15:04 bwarsaw Exp $ $Id: ClientStorage.py,v 1.47 2002/08/16 21:42:49 tim_one Exp $
""" """
import cPickle import cPickle
...@@ -117,7 +117,7 @@ class ClientStorage: ...@@ -117,7 +117,7 @@ class ClientStorage:
# Mutual exclusion is achieved using tpc_cond(), which # Mutual exclusion is achieved using tpc_cond(), which
# protects _transaction. A thread that wants to assign to # protects _transaction. A thread that wants to assign to
# self._transaction must acquire tpc_cond() first. # self._transaction must acquire tpc_cond() first.
# Invariant: If self._transaction is not None, then tpc_cond() # Invariant: If self._transaction is not None, then tpc_cond()
# must be acquired. # must be acquired.
self.tpc_cond = threading.Condition() self.tpc_cond = threading.Condition()
...@@ -339,22 +339,20 @@ class ClientStorage: ...@@ -339,22 +339,20 @@ class ClientStorage:
def tpc_begin(self, transaction, tid=None, status=' '): def tpc_begin(self, transaction, tid=None, status=' '):
self.tpc_cond.acquire() self.tpc_cond.acquire()
while self._transaction is not None: while self._transaction is not None:
# It is allowable for a client to call two tpc_begins in a
# row with the same transaction, and the second of these
# must be ignored.
if self._transaction == transaction: if self._transaction == transaction:
# Our tpc_cond lock is re-entrant. It is allowable for a
# client to call two tpc_begins in a row with the same
# transaction, and the second of these must be ignored. Our
# locking is safe because the acquire() above gives us a
# second lock on tpc_cond, and the following release() brings
# us back to owning just the one tpc_cond lock (acquired
# during the first of two consecutive tpc_begins).
self.tpc_cond.release() self.tpc_cond.release()
return return
self.tpc_cond.wait() self.tpc_cond.wait()
self.tpc_cond.release()
if self._server is None: if self._server is None:
self.tpc_cond.release() # XXX Why set _transaction to None? It must be None now, else
# XXX we would have stayed in the while loop.
assert self._transaction is None
self._transaction = None self._transaction = None
self.tpc_cond.release()
raise ClientDisconnected() raise ClientDisconnected()
if tid is None: if tid is None:
...@@ -363,7 +361,13 @@ class ClientStorage: ...@@ -363,7 +361,13 @@ class ClientStorage:
else: else:
self._ts = TimeStamp(tid) self._ts = TimeStamp(tid)
id = tid id = tid
# XXX Can setting _transaction be moved above the "id=" business?
# XXX We want to hold the condvar across as little code as possible,
# XXX to slash the chances for deadlock (among other things); e.g.,
# XXX if one of those timestamp routines raised an exception, we'd
# XXX hold the condvar forever.
self._transaction = transaction self._transaction = transaction
self.tpc_cond.release()
try: try:
r = self._server.tpc_begin(id, r = self._server.tpc_begin(id,
...@@ -373,10 +377,10 @@ class ClientStorage: ...@@ -373,10 +377,10 @@ class ClientStorage:
tid, status) tid, status)
except: except:
# Client may have disconnected during the tpc_begin(). # Client may have disconnected during the tpc_begin().
# Then notifyDisconnected() will have released the lock.
if self._server is not disconnected_stub: if self._server is not disconnected_stub:
self.tpc_cond.acquire() self.tpc_cond.acquire()
self._transaction = None self._transaction = None
self.tpc_cond.notify()
self.tpc_cond.release() self.tpc_cond.release()
raise raise
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment