Skip to content

Commit a924fc7

Browse files
committed
Issue python#21565: multiprocessing: use contex-manager protocol for synchronization
primitives.
1 parent 1691e35 commit a924fc7

File tree

9 files changed

+43
-102
lines changed

9 files changed

+43
-102
lines changed

Doc/library/multiprocessing.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,9 @@ processes.
13201320
Note that accessing the ctypes object through the wrapper can be a lot slower
13211321
than accessing the raw ctypes object.
13221322

1323+
.. versionchanged:: 3.5
1324+
Synchronized objects support the :term:`context manager` protocol.
1325+
13231326

13241327
The table below compares the syntax for creating shared ctypes objects from
13251328
shared memory with the normal ctypes syntax. (In the table ``MyStruct`` is some

Lib/multiprocessing/dummy/connection.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,8 @@ def poll(self, timeout=0.0):
5959
return True
6060
if timeout <= 0.0:
6161
return False
62-
self._in.not_empty.acquire()
63-
self._in.not_empty.wait(timeout)
64-
self._in.not_empty.release()
62+
with self._in.not_empty:
63+
self._in.not_empty.wait(timeout)
6564
return self._in.qsize() > 0
6665

6766
def close(self):

Lib/multiprocessing/heap.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,8 @@ def malloc(self, size):
216216
assert 0 <= size < sys.maxsize
217217
if os.getpid() != self._lastpid:
218218
self.__init__() # reinitialize after fork
219-
self._lock.acquire()
220-
self._free_pending_blocks()
221-
try:
219+
with self._lock:
220+
self._free_pending_blocks()
222221
size = self._roundup(max(size,1), self._alignment)
223222
(arena, start, stop) = self._malloc(size)
224223
new_stop = start + size
@@ -227,8 +226,6 @@ def malloc(self, size):
227226
block = (arena, start, new_stop)
228227
self._allocated_blocks.add(block)
229228
return block
230-
finally:
231-
self._lock.release()
232229

233230
#
234231
# Class representing a chunk of an mmap -- can be inherited by child process

Lib/multiprocessing/managers.py

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,7 @@ def debug_info(self, c):
306306
'''
307307
Return some info --- useful to spot problems with refcounting
308308
'''
309-
self.mutex.acquire()
310-
try:
309+
with self.mutex:
311310
result = []
312311
keys = list(self.id_to_obj.keys())
313312
keys.sort()
@@ -317,8 +316,6 @@ def debug_info(self, c):
317316
(ident, self.id_to_refcount[ident],
318317
str(self.id_to_obj[ident][0])[:75]))
319318
return '\n'.join(result)
320-
finally:
321-
self.mutex.release()
322319

323320
def number_of_objects(self, c):
324321
'''
@@ -343,8 +340,7 @@ def create(self, c, typeid, *args, **kwds):
343340
'''
344341
Create a new shared object and return its id
345342
'''
346-
self.mutex.acquire()
347-
try:
343+
with self.mutex:
348344
callable, exposed, method_to_typeid, proxytype = \
349345
self.registry[typeid]
350346

@@ -374,8 +370,6 @@ def create(self, c, typeid, *args, **kwds):
374370
# has been created.
375371
self.incref(c, ident)
376372
return ident, tuple(exposed)
377-
finally:
378-
self.mutex.release()
379373

380374
def get_methods(self, c, token):
381375
'''
@@ -392,22 +386,16 @@ def accept_connection(self, c, name):
392386
self.serve_client(c)
393387

394388
def incref(self, c, ident):
395-
self.mutex.acquire()
396-
try:
389+
with self.mutex:
397390
self.id_to_refcount[ident] += 1
398-
finally:
399-
self.mutex.release()
400391

401392
def decref(self, c, ident):
402-
self.mutex.acquire()
403-
try:
393+
with self.mutex:
404394
assert self.id_to_refcount[ident] >= 1
405395
self.id_to_refcount[ident] -= 1
406396
if self.id_to_refcount[ident] == 0:
407397
del self.id_to_obj[ident], self.id_to_refcount[ident]
408398
util.debug('disposing of obj with id %r', ident)
409-
finally:
410-
self.mutex.release()
411399

412400
#
413401
# Class to represent state of a manager
@@ -671,14 +659,11 @@ class BaseProxy(object):
671659

672660
def __init__(self, token, serializer, manager=None,
673661
authkey=None, exposed=None, incref=True):
674-
BaseProxy._mutex.acquire()
675-
try:
662+
with BaseProxy._mutex:
676663
tls_idset = BaseProxy._address_to_local.get(token.address, None)
677664
if tls_idset is None:
678665
tls_idset = util.ForkAwareLocal(), ProcessLocalSet()
679666
BaseProxy._address_to_local[token.address] = tls_idset
680-
finally:
681-
BaseProxy._mutex.release()
682667

683668
# self._tls is used to record the connection used by this
684669
# thread to communicate with the manager at token.address

Lib/multiprocessing/pool.py

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -666,8 +666,7 @@ def __iter__(self):
666666
return self
667667

668668
def next(self, timeout=None):
669-
self._cond.acquire()
670-
try:
669+
with self._cond:
671670
try:
672671
item = self._items.popleft()
673672
except IndexError:
@@ -680,8 +679,6 @@ def next(self, timeout=None):
680679
if self._index == self._length:
681680
raise StopIteration
682681
raise TimeoutError
683-
finally:
684-
self._cond.release()
685682

686683
success, value = item
687684
if success:
@@ -691,8 +688,7 @@ def next(self, timeout=None):
691688
__next__ = next # XXX
692689

693690
def _set(self, i, obj):
694-
self._cond.acquire()
695-
try:
691+
with self._cond:
696692
if self._index == i:
697693
self._items.append(obj)
698694
self._index += 1
@@ -706,18 +702,13 @@ def _set(self, i, obj):
706702

707703
if self._index == self._length:
708704
del self._cache[self._job]
709-
finally:
710-
self._cond.release()
711705

712706
def _set_length(self, length):
713-
self._cond.acquire()
714-
try:
707+
with self._cond:
715708
self._length = length
716709
if self._index == self._length:
717710
self._cond.notify()
718711
del self._cache[self._job]
719-
finally:
720-
self._cond.release()
721712

722713
#
723714
# Class whose instances are returned by `Pool.imap_unordered()`
@@ -726,15 +717,12 @@ def _set_length(self, length):
726717
class IMapUnorderedIterator(IMapIterator):
727718

728719
def _set(self, i, obj):
729-
self._cond.acquire()
730-
try:
720+
with self._cond:
731721
self._items.append(obj)
732722
self._index += 1
733723
self._cond.notify()
734724
if self._index == self._length:
735725
del self._cache[self._job]
736-
finally:
737-
self._cond.release()
738726

739727
#
740728
#
@@ -760,10 +748,7 @@ def _setup_queues(self):
760748
@staticmethod
761749
def _help_stuff_finish(inqueue, task_handler, size):
762750
# put sentinels at head of inqueue to make workers finish
763-
inqueue.not_empty.acquire()
764-
try:
751+
with inqueue.not_empty:
765752
inqueue.queue.clear()
766753
inqueue.queue.extend([None] * size)
767754
inqueue.not_empty.notify_all()
768-
finally:
769-
inqueue.not_empty.release()

Lib/multiprocessing/queues.py

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,11 @@ def put(self, obj, block=True, timeout=None):
8181
if not self._sem.acquire(block, timeout):
8282
raise Full
8383

84-
self._notempty.acquire()
85-
try:
84+
with self._notempty:
8685
if self._thread is None:
8786
self._start_thread()
8887
self._buffer.append(obj)
8988
self._notempty.notify()
90-
finally:
91-
self._notempty.release()
9289

9390
def get(self, block=True, timeout=None):
9491
if block and timeout is None:
@@ -201,12 +198,9 @@ def _finalize_join(twr):
201198
@staticmethod
202199
def _finalize_close(buffer, notempty):
203200
debug('telling queue thread to quit')
204-
notempty.acquire()
205-
try:
201+
with notempty:
206202
buffer.append(_sentinel)
207203
notempty.notify()
208-
finally:
209-
notempty.release()
210204

211205
@staticmethod
212206
def _feed(buffer, notempty, send_bytes, writelock, close, ignore_epipe):
@@ -295,35 +289,24 @@ def put(self, obj, block=True, timeout=None):
295289
if not self._sem.acquire(block, timeout):
296290
raise Full
297291

298-
self._notempty.acquire()
299-
self._cond.acquire()
300-
try:
292+
with self._notempty, self._cond:
301293
if self._thread is None:
302294
self._start_thread()
303295
self._buffer.append(obj)
304296
self._unfinished_tasks.release()
305297
self._notempty.notify()
306-
finally:
307-
self._cond.release()
308-
self._notempty.release()
309298

310299
def task_done(self):
311-
self._cond.acquire()
312-
try:
300+
with self._cond:
313301
if not self._unfinished_tasks.acquire(False):
314302
raise ValueError('task_done() called too many times')
315303
if self._unfinished_tasks._semlock._is_zero():
316304
self._cond.notify_all()
317-
finally:
318-
self._cond.release()
319305

320306
def join(self):
321-
self._cond.acquire()
322-
try:
307+
with self._cond:
323308
if not self._unfinished_tasks._semlock._is_zero():
324309
self._cond.wait()
325-
finally:
326-
self._cond.release()
327310

328311
#
329312
# Simplified Queue type -- really just a locked pipe

Lib/multiprocessing/sharedctypes.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ def __init__(self, obj, lock=None, ctx=None):
188188
self.acquire = self._lock.acquire
189189
self.release = self._lock.release
190190

191+
def __enter__(self):
192+
return self._lock.__enter__()
193+
194+
def __exit__(self, *args):
195+
return self._lock.__exit__(*args)
196+
191197
def __reduce__(self):
192198
assert_spawning(self)
193199
return synchronized, (self._obj, self._lock)
@@ -212,32 +218,20 @@ def __len__(self):
212218
return len(self._obj)
213219

214220
def __getitem__(self, i):
215-
self.acquire()
216-
try:
221+
with self:
217222
return self._obj[i]
218-
finally:
219-
self.release()
220223

221224
def __setitem__(self, i, value):
222-
self.acquire()
223-
try:
225+
with self:
224226
self._obj[i] = value
225-
finally:
226-
self.release()
227227

228228
def __getslice__(self, start, stop):
229-
self.acquire()
230-
try:
229+
with self:
231230
return self._obj[start:stop]
232-
finally:
233-
self.release()
234231

235232
def __setslice__(self, start, stop, values):
236-
self.acquire()
237-
try:
233+
with self:
238234
self._obj[start:stop] = values
239-
finally:
240-
self.release()
241235

242236

243237
class SynchronizedString(SynchronizedArray):

Lib/multiprocessing/synchronize.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -337,34 +337,24 @@ def __init__(self, *, ctx):
337337
self._flag = ctx.Semaphore(0)
338338

339339
def is_set(self):
340-
self._cond.acquire()
341-
try:
340+
with self._cond:
342341
if self._flag.acquire(False):
343342
self._flag.release()
344343
return True
345344
return False
346-
finally:
347-
self._cond.release()
348345

349346
def set(self):
350-
self._cond.acquire()
351-
try:
347+
with self._cond:
352348
self._flag.acquire(False)
353349
self._flag.release()
354350
self._cond.notify_all()
355-
finally:
356-
self._cond.release()
357351

358352
def clear(self):
359-
self._cond.acquire()
360-
try:
353+
with self._cond:
361354
self._flag.acquire(False)
362-
finally:
363-
self._cond.release()
364355

365356
def wait(self, timeout=None):
366-
self._cond.acquire()
367-
try:
357+
with self._cond:
368358
if self._flag.acquire(False):
369359
self._flag.release()
370360
else:
@@ -374,8 +364,6 @@ def wait(self, timeout=None):
374364
self._flag.release()
375365
return True
376366
return False
377-
finally:
378-
self._cond.release()
379367

380368
#
381369
# Barrier

Lib/multiprocessing/util.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,13 @@ def _reset(self):
327327
self.acquire = self._lock.acquire
328328
self.release = self._lock.release
329329

330+
def __enter__(self):
331+
return self._lock.__enter__()
332+
333+
def __exit__(self, *args):
334+
return self._lock.__exit__(*args)
335+
336+
330337
class ForkAwareLocal(threading.local):
331338
def __init__(self):
332339
register_after_fork(self, lambda obj : obj.__dict__.clear())

0 commit comments

Comments
 (0)