1 """Synchronization metaclass.
3 This metaclass makes it possible to declare synchronized methods.
9 # First we need to define a reentrant lock.
10 # This is generally useful and should probably be in a standard Python
11 # library module. For now, we in-line it.
17 This is a mutex-like object which can be acquired by the same
18 thread more than once. It keeps a reference count of the number
19 of times it has been acquired by the same thread. Each acquire()
20 call must be matched by a release() call and only the last
21 release() call actually releases the lock for acquisition by
24 The implementation uses two locks internally:
26 __mutex is a short term lock used to protect the instance variables
27 __wait is the lock for which other threads wait
29 A thread intending to acquire both locks should acquire __wait
32 The implementation uses two other instance variables, protected by
35 __tid is the thread ID of the thread that currently has the lock
36 __count is the number of times the current thread has acquired it
38 When the lock is released, __tid is None and __count is zero.
43 """Constructor. Initialize all instance variables."""
44 self
.__mutex
= thread
.allocate_lock()
45 self
.__wait
= thread
.allocate_lock()
49 def acquire(self
, flag
=1):
52 If the optional flag argument is false, returns immediately
53 when it cannot acquire the __wait lock without blocking (it
54 may still block for a little while in order to acquire the
57 The return value is only relevant when the flag argument is
58 false; it is 1 if the lock is acquired, 0 if not.
61 self
.__mutex
.acquire()
63 if self
.__tid
== thread
.get_ident():
64 self
.__count
= self
.__count
+ 1
67 self
.__mutex
.release()
68 locked
= self
.__wait
.acquire(flag
)
69 if not flag
and not locked
:
72 self
.__mutex
.acquire()
73 assert self
.__tid
== None
74 assert self
.__count
== 0
75 self
.__tid
= thread
.get_ident()
79 self
.__mutex
.release()
84 If this thread doesn't currently have the lock, an assertion
87 Only allow another thread to acquire the lock when the count
88 reaches zero after decrementing it.
91 self
.__mutex
.acquire()
93 assert self
.__tid
== thread
.get_ident()
94 assert self
.__count
> 0
95 self
.__count
= self
.__count
- 1
100 self
.__mutex
.release()
107 def f2(lock
, done
=done
):
109 print "f2 running in thread %d\n" % thread
.get_ident(),
113 def f1(lock
, f2
=f2
, done
=done
):
115 print "f1 running in thread %d\n" % thread
.get_ident(),
124 f1(lock
) # Adds 2 to done
129 thread
.start_new_thread(f1
, (lock
,)) # Adds 2
130 thread
.start_new_thread(f1
, (lock
, f1
)) # Adds 3
131 thread
.start_new_thread(f2
, (lock
,)) # Adds 1
132 thread
.start_new_thread(f2
, (lock
,)) # Adds 1
142 # Now, the Locking metaclass is a piece of cake.
143 # As an example feature, methods whose name begins with exactly one
144 # underscore are not synchronized.
146 from Meta
import MetaClass
, MetaHelper
, MetaMethodWrapper
148 class LockingMethodWrapper(MetaMethodWrapper
):
149 def __call__(self
, *args
, **kw
):
150 if self
.__name
__[:1] == '_' and self
.__name
__[1:] != '_':
151 return apply(self
.func
, (self
.inst
,) + args
, kw
)
152 self
.inst
.__lock
__.acquire()
154 return apply(self
.func
, (self
.inst
,) + args
, kw
)
156 self
.inst
.__lock
__.release()
158 class LockingHelper(MetaHelper
):
159 __methodwrapper__
= LockingMethodWrapper
160 def __helperinit__(self
, formalclass
):
161 MetaHelper
.__helperinit
__(self
, formalclass
)
162 self
.__lock
__ = Lock()
164 class LockingMetaClass(MetaClass
):
165 __helper__
= LockingHelper
167 Locking
= LockingMetaClass('Locking', (), {})
170 # For kicks, take away the Locking base class and see it die
171 class Buffer(Locking
):
172 def __init__(self
, initialsize
):
173 assert initialsize
> 0
174 self
.size
= initialsize
175 self
.buffer = [None]*self
.size
176 self
.first
= self
.last
= 0
178 # Do we need to grow the buffer?
179 if (self
.last
+1) % self
.size
!= self
.first
:
180 # Insert the new item
181 self
.buffer[self
.last
] = item
182 self
.last
= (self
.last
+1) % self
.size
184 # Double the buffer size
185 # First normalize it so that first==0 and last==size-1
186 print "buffer =", self
.buffer
187 print "first = %d, last = %d, size = %d" % (
188 self
.first
, self
.last
, self
.size
)
189 if self
.first
<= self
.last
:
190 temp
= self
.buffer[self
.first
:self
.last
]
192 temp
= self
.buffer[self
.first
:] + self
.buffer[:self
.last
]
194 self
.buffer = temp
+ [None]*(self
.size
+1)
196 self
.last
= self
.size
-1
197 self
.size
= self
.size
*2
198 print "Buffer size doubled to", self
.size
199 print "new buffer =", self
.buffer
200 print "first = %d, last = %d, size = %d" % (
201 self
.first
, self
.last
, self
.size
)
202 self
.put(item
) # Recursive call to test the locking
204 # Is the buffer empty?
205 if self
.first
== self
.last
:
206 raise EOFError # Avoid defining a new exception
207 item
= self
.buffer[self
.first
]
208 self
.first
= (self
.first
+1) % self
.size
211 def producer(buffer, wait
, n
=1000):
218 print "Producer: done producing", n
, "items"
221 def consumer(buffer, wait
, n
=1000):
229 raise AssertionError, \
230 "get() returned %s, expected %s" % (x
, i
)
237 print "Consumer: done consuming", n
, "items"
240 pwait
= thread
.allocate_lock()
242 cwait
= thread
.allocate_lock()
246 thread
.start_new_thread(consumer
, (buffer, cwait
, n
))
247 thread
.start_new_thread(producer
, (buffer, pwait
, n
))
249 print "Producer done"
252 print "buffer size ==", len(buffer.buffer)
254 if __name__
== '__main__':