Home | History | Annotate | Download | only in multiprocessing
      1 #
      2 # Module implementing synchronization primitives
      3 #
      4 # multiprocessing/synchronize.py
      5 #
      6 # 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.
     33 #
     34 
     35 __all__ = [
     36     'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event'
     37     ]
     38 
     39 import threading
     40 import os
     41 import sys
     42 
     43 from time import time as _time, sleep as _sleep
     44 
     45 import _multiprocessing
     46 from multiprocessing.process import current_process
     47 from multiprocessing.util import Finalize, register_after_fork, debug
     48 from multiprocessing.forking import assert_spawning, Popen
     49 
     50 # 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
     53 try:
     54     from _multiprocessing import SemLock
     55 except (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 
     61 #
     62 # Constants
     63 #
     64 
     65 RECURSIVE_MUTEX, SEMAPHORE = range(2)
     66 SEM_VALUE_MAX = _multiprocessing.SemLock.SEM_VALUE_MAX
     67 
     68 #
     69 # Base class for semaphores and mutexes; wraps `_multiprocessing.SemLock`
     70 #
     71 
     72 class 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
     87 
     88     def __enter__(self):
     89         return self._semlock.__enter__()
     90 
     91     def __exit__(self, *args):
     92         return self._semlock.__exit__(*args)
     93 
     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 
    108 class Semaphore(SemLock):
    109 
    110     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 
    127 class BoundedSemaphore(Semaphore):
    128 
    129     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 
    144 class Lock(SemLock):
    145 
    146     def __init__(self):
    147         SemLock.__init__(self, SEMAPHORE, 1, 1)
    148 
    149     def __repr__(self):
    150         try:
    151             if self._semlock._is_mine():
    152                 name = current_process().name
    153                 if threading.current_thread().name != 'MainThread':
    154                     name += '|' + threading.current_thread().name
    155             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 
    169 class RLock(SemLock):
    170 
    171     def __init__(self):
    172         SemLock.__init__(self, RECURSIVE_MUTEX, 1, 1)
    173 
    174     def __repr__(self):
    175         try:
    176             if self._semlock._is_mine():
    177                 name = current_process().name
    178                 if threading.current_thread().name != 'MainThread':
    179                     name += '|' + threading.current_thread().name
    180                 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 
    195 class 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 
    214     def __enter__(self):
    215         return self._lock.__enter__()
    216 
    217     def __exit__(self, *args):
    218         return self._lock.__exit__(*args)
    219 
    220     def _make_methods(self):
    221         self.acquire = self._lock.acquire
    222         self.release = self._lock.release
    223 
    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:
    229             num_waiters = 'unkown'
    230         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 
    299 class Event(object):
    300 
    301     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)
    338 
    339             if self._flag.acquire(False):
    340                 self._flag.release()
    341                 return True
    342             return False
    343         finally:
    344             self._cond.release()
    345