blob: d845f72a1c4b80ac70b98fffdb508471a5794c7e [file] [log] [blame]
Benjamin Peterson7f03ea72008-06-13 19:20:48 +00001#
2# Module implementing synchronization primitives
3#
4# multiprocessing/synchronize.py
5#
R. David Murray79af2452010-12-14 01:42:40 +00006# Copyright (c) 2006-2008, R Oudkerk
7# All rights reserved.
8#
9# Redistribution and use in source and binary forms, with or without
10# modification, are permitted provided that the following conditions
11# are met:
12#
13# 1. Redistributions of source code must retain the above copyright
14# notice, this list of conditions and the following disclaimer.
15# 2. Redistributions in binary form must reproduce the above copyright
16# notice, this list of conditions and the following disclaimer in the
17# documentation and/or other materials provided with the distribution.
18# 3. Neither the name of author nor the names of any contributors may be
19# used to endorse or promote products derived from this software
20# without specific prior written permission.
21#
22# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
23# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32# SUCH DAMAGE.
Benjamin Peterson7f03ea72008-06-13 19:20:48 +000033#
34
35__all__ = [
36 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event'
37 ]
38
39import threading
40import os
41import sys
42
43from time import time as _time, sleep as _sleep
44
45import _multiprocessing
46from multiprocessing.process import current_process
47from multiprocessing.util import Finalize, register_after_fork, debug
48from multiprocessing.forking import assert_spawning, Popen
49
Jesse Noller37040cd2008-09-30 00:15:45 +000050# Try to import the mp.synchronize module cleanly, if it fails
51# raise ImportError for platforms lacking a working sem_open implementation.
52# See issue 3770
53try:
54 from _multiprocessing import SemLock
55except (ImportError):
56 raise ImportError("This platform lacks a functioning sem_open" +
57 " implementation, therefore, the required" +
58 " synchronization primitives needed will not" +
59 " function, see issue 3770.")
60
Benjamin Peterson7f03ea72008-06-13 19:20:48 +000061#
62# Constants
63#
64
65RECURSIVE_MUTEX, SEMAPHORE = range(2)
66SEM_VALUE_MAX = _multiprocessing.SemLock.SEM_VALUE_MAX
67
68#
69# Base class for semaphores and mutexes; wraps `_multiprocessing.SemLock`
70#
71
72class SemLock(object):
73
74 def __init__(self, kind, value, maxvalue):
75 sl = self._semlock = _multiprocessing.SemLock(kind, value, maxvalue)
76 debug('created semlock with handle %s' % sl.handle)
77 self._make_methods()
78
79 if sys.platform != 'win32':
80 def _after_fork(obj):
81 obj._semlock._after_fork()
82 register_after_fork(self, _after_fork)
83
84 def _make_methods(self):
85 self.acquire = self._semlock.acquire
86 self.release = self._semlock.release
Benjamin Petersoneb88cb82009-05-31 15:00:27 +000087
88 def __enter__(self):
89 return self._semlock.__enter__()
90
91 def __exit__(self, *args):
92 return self._semlock.__exit__(*args)
Benjamin Peterson7f03ea72008-06-13 19:20:48 +000093
94 def __getstate__(self):
95 assert_spawning(self)
96 sl = self._semlock
97 return (Popen.duplicate_for_child(sl.handle), sl.kind, sl.maxvalue)
98
99 def __setstate__(self, state):
100 self._semlock = _multiprocessing.SemLock._rebuild(*state)
101 debug('recreated blocker with handle %r' % state[0])
102 self._make_methods()
103
104#
105# Semaphore
106#
107
108class Semaphore(SemLock):
Benjamin Petersonf7feaec2008-09-01 17:10:46 +0000109
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000110 def __init__(self, value=1):
111 SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX)
112
113 def get_value(self):
114 return self._semlock._get_value()
115
116 def __repr__(self):
117 try:
118 value = self._semlock._get_value()
119 except Exception:
120 value = 'unknown'
121 return '<Semaphore(value=%s)>' % value
122
123#
124# Bounded semaphore
125#
126
127class BoundedSemaphore(Semaphore):
Benjamin Petersonf7feaec2008-09-01 17:10:46 +0000128
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000129 def __init__(self, value=1):
130 SemLock.__init__(self, SEMAPHORE, value, value)
131
132 def __repr__(self):
133 try:
134 value = self._semlock._get_value()
135 except Exception:
136 value = 'unknown'
137 return '<BoundedSemaphore(value=%s, maxvalue=%s)>' % \
138 (value, self._semlock.maxvalue)
139
140#
141# Non-recursive lock
142#
143
144class Lock(SemLock):
Benjamin Petersonf7feaec2008-09-01 17:10:46 +0000145
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000146 def __init__(self):
147 SemLock.__init__(self, SEMAPHORE, 1, 1)
148
149 def __repr__(self):
150 try:
151 if self._semlock._is_mine():
Jesse Noller5bc9f4c2008-08-19 19:06:19 +0000152 name = current_process().name
153 if threading.current_thread().name != 'MainThread':
154 name += '|' + threading.current_thread().name
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000155 elif self._semlock._get_value() == 1:
156 name = 'None'
157 elif self._semlock._count() > 0:
158 name = 'SomeOtherThread'
159 else:
160 name = 'SomeOtherProcess'
161 except Exception:
162 name = 'unknown'
163 return '<Lock(owner=%s)>' % name
164
165#
166# Recursive lock
167#
168
169class RLock(SemLock):
Benjamin Petersonf7feaec2008-09-01 17:10:46 +0000170
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000171 def __init__(self):
172 SemLock.__init__(self, RECURSIVE_MUTEX, 1, 1)
173
174 def __repr__(self):
175 try:
176 if self._semlock._is_mine():
Jesse Noller5bc9f4c2008-08-19 19:06:19 +0000177 name = current_process().name
178 if threading.current_thread().name != 'MainThread':
179 name += '|' + threading.current_thread().name
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000180 count = self._semlock._count()
181 elif self._semlock._get_value() == 1:
182 name, count = 'None', 0
183 elif self._semlock._count() > 0:
184 name, count = 'SomeOtherThread', 'nonzero'
185 else:
186 name, count = 'SomeOtherProcess', 'nonzero'
187 except Exception:
188 name, count = 'unknown', 'unknown'
189 return '<RLock(%s, %s)>' % (name, count)
190
191#
192# Condition variable
193#
194
195class Condition(object):
196
197 def __init__(self, lock=None):
198 self._lock = lock or RLock()
199 self._sleeping_count = Semaphore(0)
200 self._woken_count = Semaphore(0)
201 self._wait_semaphore = Semaphore(0)
202 self._make_methods()
203
204 def __getstate__(self):
205 assert_spawning(self)
206 return (self._lock, self._sleeping_count,
207 self._woken_count, self._wait_semaphore)
208
209 def __setstate__(self, state):
210 (self._lock, self._sleeping_count,
211 self._woken_count, self._wait_semaphore) = state
212 self._make_methods()
213
Benjamin Petersoneb88cb82009-05-31 15:00:27 +0000214 def __enter__(self):
215 return self._lock.__enter__()
216
217 def __exit__(self, *args):
218 return self._lock.__exit__(*args)
219
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000220 def _make_methods(self):
221 self.acquire = self._lock.acquire
222 self.release = self._lock.release
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000223
224 def __repr__(self):
225 try:
226 num_waiters = (self._sleeping_count._semlock._get_value() -
227 self._woken_count._semlock._get_value())
228 except Exception:
doko@ubuntu.com98552492013-05-15 16:54:16 +0200229 num_waiters = 'unknown'
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000230 return '<Condition(%s, %s)>' % (self._lock, num_waiters)
231
232 def wait(self, timeout=None):
233 assert self._lock._semlock._is_mine(), \
234 'must acquire() condition before using wait()'
235
236 # indicate that this thread is going to sleep
237 self._sleeping_count.release()
238
239 # release lock
240 count = self._lock._semlock._count()
241 for i in xrange(count):
242 self._lock.release()
243
244 try:
245 # wait for notification or timeout
246 self._wait_semaphore.acquire(True, timeout)
247 finally:
248 # indicate that this thread has woken
249 self._woken_count.release()
250
251 # reacquire lock
252 for i in xrange(count):
253 self._lock.acquire()
254
255 def notify(self):
256 assert self._lock._semlock._is_mine(), 'lock is not owned'
257 assert not self._wait_semaphore.acquire(False)
258
259 # to take account of timeouts since last notify() we subtract
260 # woken_count from sleeping_count and rezero woken_count
261 while self._woken_count.acquire(False):
262 res = self._sleeping_count.acquire(False)
263 assert res
264
265 if self._sleeping_count.acquire(False): # try grabbing a sleeper
266 self._wait_semaphore.release() # wake up one sleeper
267 self._woken_count.acquire() # wait for the sleeper to wake
268
269 # rezero _wait_semaphore in case a timeout just happened
270 self._wait_semaphore.acquire(False)
271
272 def notify_all(self):
273 assert self._lock._semlock._is_mine(), 'lock is not owned'
274 assert not self._wait_semaphore.acquire(False)
275
276 # to take account of timeouts since last notify*() we subtract
277 # woken_count from sleeping_count and rezero woken_count
278 while self._woken_count.acquire(False):
279 res = self._sleeping_count.acquire(False)
280 assert res
281
282 sleepers = 0
283 while self._sleeping_count.acquire(False):
284 self._wait_semaphore.release() # wake up one sleeper
285 sleepers += 1
286
287 if sleepers:
288 for i in xrange(sleepers):
289 self._woken_count.acquire() # wait for a sleeper to wake
290
291 # rezero wait_semaphore in case some timeouts just happened
292 while self._wait_semaphore.acquire(False):
293 pass
294
295#
296# Event
297#
298
299class Event(object):
Benjamin Petersonf7feaec2008-09-01 17:10:46 +0000300
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000301 def __init__(self):
302 self._cond = Condition(Lock())
303 self._flag = Semaphore(0)
304
305 def is_set(self):
306 self._cond.acquire()
307 try:
308 if self._flag.acquire(False):
309 self._flag.release()
310 return True
311 return False
312 finally:
313 self._cond.release()
314
315 def set(self):
316 self._cond.acquire()
317 try:
318 self._flag.acquire(False)
319 self._flag.release()
320 self._cond.notify_all()
321 finally:
322 self._cond.release()
323
324 def clear(self):
325 self._cond.acquire()
326 try:
327 self._flag.acquire(False)
328 finally:
329 self._cond.release()
330
331 def wait(self, timeout=None):
332 self._cond.acquire()
333 try:
334 if self._flag.acquire(False):
335 self._flag.release()
336 else:
337 self._cond.wait(timeout)
Jesse Noller02cb0eb2009-04-01 03:45:50 +0000338
339 if self._flag.acquire(False):
340 self._flag.release()
341 return True
342 return False
Benjamin Peterson7f03ea72008-06-13 19:20:48 +0000343 finally:
344 self._cond.release()