_manager.py 4.26 KB
Newer Older
1 2 3 4 5 6
############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
Jim Fulton's avatar
Jim Fulton committed
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################
"""A TransactionManager controls transaction boundaries.

It coordinates application code and resource managers, so that they
are associated with the right transaction.
"""

import thread

from transaction._transaction import Transaction

24 25 26 27 28 29
# We have to remember sets of synch objects, especially Connections.
# But we don't want mere registration with a transaction manager to
# keep a synch object alive forever; in particular, it's common
# practice not to explicitly close Connection objects, and keeping
# a Connection alive keeps a potentially huge number of other objects
# alive (e.g., the cache, and everything reachable from it too).
30
# Therefore we use "weak sets" internally.
31
#
32 33
# Obscure:  because of the __init__.py maze, we can't import WeakSet
# at top level here.
34

35 36 37 38 39 40 41 42 43 44
# Call the ISynchronizer newTransaction() method on every element of
# WeakSet synchs.
# A transaction manager needs to do this whenever begin() is called.
# Since it would be good if tm.get() returned the new transaction while
# newTransaction() is running, calling this has to be delayed until after
# the transaction manager has done whatever it needs to do to make its
# get() return the new txn.
def _new_transaction(txn, synchs):
    synchs.map(lambda s: s.newTransaction(txn))

45 46 47
class TransactionManager(object):

    def __init__(self):
48 49
        from ZODB.utils import WeakSet

50
        self._txn = None
51
        self._synchs = WeakSet()
52 53 54 55

    def begin(self):
        if self._txn is not None:
            self._txn.abort()
56 57 58
        txn = self._txn = Transaction(self._synchs, self)
        _new_transaction(txn, self._synchs)
        return txn
59 60 61

    def get(self):
        if self._txn is None:
62
            self._txn = Transaction(self._synchs, self)
63 64 65 66 67 68 69
        return self._txn

    def free(self, txn):
        assert txn is self._txn
        self._txn = None

    def registerSynch(self, synch):
70
        self._synchs.add(synch)
71 72 73 74

    def unregisterSynch(self, synch):
        self._synchs.remove(synch)

75 76 77 78 79 80
    def commit(self, sub=False):
        self.get().commit(sub)

    def abort(self, sub=False):
        self.get().abort(sub)

81 82 83
    def savepoint(self, optimistic=False):
        return self.get().savepoint(optimistic)

84
class ThreadTransactionManager(TransactionManager):
85 86 87 88 89 90 91 92
    """Thread-aware transaction manager.

    Each thread is associated with a unique transaction.
    """

    def __init__(self):
        # _threads maps thread ids to transactions
        self._txns = {}
93
        # _synchs maps a thread id to a WeakSet of registered synchronizers.
94 95
        # The WeakSet is passed to the Transaction constructor, because the
        # latter needs to call the synchronizers when it commits.
96 97 98 99 100 101 102
        self._synchs = {}

    def begin(self):
        tid = thread.get_ident()
        txn = self._txns.get(tid)
        if txn is not None:
            txn.abort()
103 104
        synchs = self._synchs.get(tid)
        txn = self._txns[tid] = Transaction(synchs, self)
105
        _new_transaction(txn, synchs)
106 107 108 109 110 111
        return txn

    def get(self):
        tid = thread.get_ident()
        txn = self._txns.get(tid)
        if txn is None:
112 113
            synchs = self._synchs.get(tid)
            txn = self._txns[tid] = Transaction(synchs, self)
114 115 116 117 118 119 120 121
        return txn

    def free(self, txn):
        tid = thread.get_ident()
        assert txn is self._txns.get(tid)
        del self._txns[tid]

    def registerSynch(self, synch):
122 123
        from ZODB.utils import WeakSet

124
        tid = thread.get_ident()
125 126 127 128
        ws = self._synchs.get(tid)
        if ws is None:
            ws = self._synchs[tid] = WeakSet()
        ws.add(synch)
129 130 131

    def unregisterSynch(self, synch):
        tid = thread.get_ident()
132 133
        ws = self._synchs[tid]
        ws.remove(synch)