[pypy-commit] pypy stm-gc: Document transaction.py. Synchronize the exception behavior with module/transaction/.
arigo
noreply at buildbot.pypy.org
Sat Mar 31 18:17:15 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc
Changeset: r54112:6c835b5c43e5
Date: 2012-03-31 18:16 +0200
http://bitbucket.org/pypy/pypy/changeset/6c835b5c43e5/
Log: Document transaction.py. Synchronize the exception behavior with
module/transaction/.
diff --git a/lib_pypy/transaction.py b/lib_pypy/transaction.py
--- a/lib_pypy/transaction.py
+++ b/lib_pypy/transaction.py
@@ -5,19 +5,41 @@
print >> sys.stderr, "warning: using lib_pypy/transaction.py, the emulator"
_pending = {}
+_in_transaction = False
+
+
+class TransactionError(Exception):
+ pass
+
def set_num_threads(num):
- pass
+ """Set the number of threads to use. In a real implementation,
+ the transactions will attempt to use 'num' threads in parallel.
+ """
-def add(f, *args):
+
+def add(f, *args, **kwds):
+ """Register the call 'f(*args, **kwds)' as running a new
+ transaction. If we are currently running in a transaction too, the
+ new transaction will only start after the end of the current
+ transaction. Note that if the same or another transaction raises an
+ exception in the meantime, all pending transactions are cancelled.
+ """
r = random.random()
assert r not in _pending # very bad luck if it is
- _pending[r] = (f, args)
+ _pending[r] = (f, args, kwds)
+
def add_epoll(ep, callback):
- for key, (f, args) in _pending.items():
+ """Register the epoll object (from the 'select' module). For any
+ event (fd, events) detected by 'ep', a new transaction will be
+ started invoking 'callback(fd, events)'. Note that all fds should
+ be registered with the flag select.EPOLLONESHOT, and re-registered
+ from the callback if needed.
+ """
+ for key, (f, args, kwds) in _pending.items():
if getattr(f, '_reads_from_epoll_', None) is ep:
- raise ValueError("add_epoll(ep): ep is already registered")
+ raise TransactionError("add_epoll(ep): ep is already registered")
def poll_reader():
# assume only one epoll is added. If the _pending list is
# now empty, wait. If not, then just poll non-blockingly.
@@ -33,18 +55,33 @@
add(poll_reader)
def remove_epoll(ep):
- for key, (f, args) in _pending.items():
+ """Explicitly unregister the epoll object. Note that raising an
+ exception in a transaction also cancels any add_epoll().
+ """
+ for key, (f, args, kwds) in _pending.items():
if getattr(f, '_reads_from_epoll_', None) is ep:
del _pending[key]
break
else:
- raise ValueError("remove_epoll(ep): ep is not registered")
+ raise TransactionError("remove_epoll(ep): ep is not registered")
def run():
+ """Run the pending transactions, as well as all transactions started
+ by them, and so on. The order is random and undeterministic. Must
+ be called from the main program, i.e. not from within another
+ transaction. If at some point all transactions are done, returns.
+ If a transaction raises an exception, it propagates here; in this
+ case all pending transactions are cancelled.
+ """
+ global _pending, _in_transaction
+ if _in_transaction:
+ raise TransactionError("recursive invocation of transaction.run()")
pending = _pending
try:
+ _in_transaction = True
while pending:
- _, (f, args) = pending.popitem()
- f(*args)
+ _, (f, args, kwds) = pending.popitem()
+ f(*args, **kwds)
finally:
+ _in_transaction = False
pending.clear() # this is the behavior we get with interp_transaction
diff --git a/pypy/module/transaction/interp_epoll.py b/pypy/module/transaction/interp_epoll.py
--- a/pypy/module/transaction/interp_epoll.py
+++ b/pypy/module/transaction/interp_epoll.py
@@ -112,7 +112,7 @@
if state.epolls is None:
state.epolls = {}
elif epoller in state.epolls:
- raise OperationError(space.w_ValueError,
+ raise OperationError(state.w_error,
space.wrap("add_epoll(ep): ep is already registered"))
pending = EPollPending(space, epoller, w_callback)
state.epolls[epoller] = pending
@@ -126,7 +126,7 @@
else:
pending = state.epolls.get(epoller, None)
if pending is None:
- raise OperationError(space.w_ValueError,
+ raise OperationError(state.w_error,
space.wrap("remove_epoll(ep): ep is not registered"))
pending.force_quit = True
del state.epolls[epoller]
diff --git a/pypy/module/transaction/test/test_epoll.py b/pypy/module/transaction/test/test_epoll.py
--- a/pypy/module/transaction/test/test_epoll.py
+++ b/pypy/module/transaction/test/test_epoll.py
@@ -70,6 +70,17 @@
transaction.run()
# assert didn't deadlock
+ def test_errors(self):
+ import transaction, select
+ epoller = select.epoll()
+ callback = lambda *args: not_actually_callable
+ transaction.add_epoll(epoller, callback)
+ raises(transaction.TransactionError,
+ transaction.add_epoll, epoller, callback)
+ transaction.remove_epoll(epoller)
+ raises(transaction.TransactionError,
+ transaction.remove_epoll, epoller)
+
class AppTestEpollEmulator(AppTestEpoll):
def setup_class(cls):
More information about the pypy-commit
mailing list