Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9f6d48b

Browse files
committed
Issue #10798: Reject supporting concurrent.futures if the system has
too few POSIX semaphores.
1 parent e10608c commit 9f6d48b

3 files changed

Lines changed: 87 additions & 72 deletions

File tree

Lib/concurrent/futures/process.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,31 @@ def _queue_manangement_worker(executor_reference,
244244
else:
245245
work_item.future.set_result(result_item.result)
246246

247+
_system_limits_checked = False
248+
_system_limited = None
249+
def _check_system_limits():
250+
global _system_limits_checked, _system_limited
251+
if _system_limits_checked:
252+
if _system_limited:
253+
raise NotImplementedError(_system_limited)
254+
_system_limits_checked = True
255+
try:
256+
import os
257+
nsems_max = os.sysconf("SC_SEM_NSEMS_MAX")
258+
except (AttributeError, ValueError):
259+
# sysconf not available or setting not available
260+
return
261+
if nsems_max == -1:
262+
# indetermine limit, assume that limit is determined
263+
# by available memory only
264+
return
265+
if nsems_max >= 256:
266+
# minimum number of semaphores available
267+
# according to POSIX
268+
return
269+
_system_limited = "system provides too few semaphores (%d available, 256 necessary)" % nsems_max
270+
raise NotImplementedError(_system_limited)
271+
247272
class ProcessPoolExecutor(_base.Executor):
248273
def __init__(self, max_workers=None):
249274
"""Initializes a new ProcessPoolExecutor instance.
@@ -253,6 +278,7 @@ def __init__(self, max_workers=None):
253278
execute the given calls. If None or not given then as many
254279
worker processes will be created as the machine has processors.
255280
"""
281+
_check_system_limits()
256282
_remove_dead_thread_references()
257283

258284
if max_workers is None:

Lib/test/test_concurrent_futures.py

Lines changed: 58 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class SECURITY_ATTRIBUTES(ctypes.Structure):
6969
assert handle is not None
7070
return handle
7171
else:
72-
event = multiprocessing.Event()
72+
event = self.Event[0]()
7373
self.CALL_LOCKS[id(event)] = event
7474
return id(event)
7575

@@ -99,7 +99,8 @@ def _signal_event(self, handle):
9999
else:
100100
self.CALL_LOCKS[handle].set()
101101

102-
def __init__(self, manual_finish=False, result=42):
102+
def __init__(self, Event, manual_finish=False, result=42):
103+
self.Event = Event
103104
self._called_event = self._create_event()
104105
self._can_finish = self._create_event()
105106

@@ -138,8 +139,8 @@ def __call__(self):
138139
raise ZeroDivisionError()
139140

140141
class MapCall(Call):
141-
def __init__(self, result=42):
142-
super().__init__(manual_finish=True, result=result)
142+
def __init__(self, Event, result=42):
143+
super().__init__(Event, manual_finish=True, result=result)
143144

144145
def __call__(self, manual_finish):
145146
if manual_finish:
@@ -155,9 +156,9 @@ def test_run_after_shutdown(self):
155156

156157

157158
def _start_some_futures(self):
158-
call1 = Call(manual_finish=True)
159-
call2 = Call(manual_finish=True)
160-
call3 = Call(manual_finish=True)
159+
call1 = Call(self.Event, manual_finish=True)
160+
call2 = Call(self.Event, manual_finish=True)
161+
call3 = Call(self.Event, manual_finish=True)
161162

162163
try:
163164
self.executor.submit(call1)
@@ -176,13 +177,28 @@ def _start_some_futures(self):
176177
call2.close()
177178
call3.close()
178179

179-
class ThreadPoolShutdownTest(ExecutorShutdownTest):
180+
class ThreadPoolMixin:
181+
# wrap in tuple to prevent creation of instance methods
182+
Event = (threading.Event,)
180183
def setUp(self):
181184
self.executor = futures.ThreadPoolExecutor(max_workers=5)
182185

183186
def tearDown(self):
184187
self.executor.shutdown(wait=True)
185188

189+
class ProcessPoolMixin:
190+
# wrap in tuple to prevent creation of instance methods
191+
Event = (multiprocessing.Event,)
192+
def setUp(self):
193+
try:
194+
self.executor = futures.ProcessPoolExecutor(max_workers=5)
195+
except NotImplementedError as e:
196+
self.skipTest(str(e))
197+
198+
def tearDown(self):
199+
self.executor.shutdown(wait=True)
200+
201+
class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest):
186202
def test_threads_terminate(self):
187203
self._start_some_futures()
188204
self.assertEqual(len(self.executor._threads), 3)
@@ -208,13 +224,7 @@ def test_del_shutdown(self):
208224
for t in threads:
209225
t.join()
210226

211-
class ProcessPoolShutdownTest(ExecutorShutdownTest):
212-
def setUp(self):
213-
self.executor = futures.ProcessPoolExecutor(max_workers=5)
214-
215-
def tearDown(self):
216-
self.executor.shutdown(wait=True)
217-
227+
class ProcessPoolShutdownTest(ProcessPoolMixin, ExecutorShutdownTest):
218228
def test_processes_terminate(self):
219229
self._start_some_futures()
220230
self.assertEqual(len(self.executor._processes), 5)
@@ -251,8 +261,8 @@ def wait_test():
251261
pass
252262
call1.set_can()
253263

254-
call1 = Call(manual_finish=True)
255-
call2 = Call(manual_finish=True)
264+
call1 = Call(self.Event, manual_finish=True)
265+
call2 = Call(self.Event, manual_finish=True)
256266
try:
257267
future1 = self.executor.submit(call1)
258268
future2 = self.executor.submit(call2)
@@ -270,7 +280,7 @@ def wait_test():
270280
call2.close()
271281

272282
def test_first_completed_one_already_completed(self):
273-
call1 = Call(manual_finish=True)
283+
call1 = Call(self.Event, manual_finish=True)
274284
try:
275285
future1 = self.executor.submit(call1)
276286

@@ -290,9 +300,9 @@ def wait_test():
290300
call1.set_can()
291301
call2.set_can()
292302

293-
call1 = Call(manual_finish=True)
294-
call2 = ExceptionCall(manual_finish=True)
295-
call3 = Call(manual_finish=True)
303+
call1 = Call(self.Event, manual_finish=True)
304+
call2 = ExceptionCall(self.Event, manual_finish=True)
305+
call3 = Call(self.Event, manual_finish=True)
296306
try:
297307
future1 = self.executor.submit(call1)
298308
future2 = self.executor.submit(call2)
@@ -317,8 +327,8 @@ def wait_test():
317327
pass
318328
call1.set_can()
319329

320-
call1 = ExceptionCall(manual_finish=True)
321-
call2 = Call(manual_finish=True)
330+
call1 = ExceptionCall(self.Event, manual_finish=True)
331+
call2 = Call(self.Event, manual_finish=True)
322332
try:
323333
future1 = self.executor.submit(call1)
324334
future2 = self.executor.submit(call2)
@@ -343,7 +353,7 @@ def wait_test():
343353
call2.close()
344354

345355
def test_first_exception_one_already_failed(self):
346-
call1 = Call(manual_finish=True)
356+
call1 = Call(self.Event, manual_finish=True)
347357
try:
348358
future1 = self.executor.submit(call1)
349359

@@ -363,8 +373,8 @@ def wait_test():
363373
call1.set_can()
364374
call2.set_can()
365375

366-
call1 = Call(manual_finish=True)
367-
call2 = Call(manual_finish=True)
376+
call1 = Call(self.Event, manual_finish=True)
377+
call2 = Call(self.Event, manual_finish=True)
368378
try:
369379
future1 = self.executor.submit(call1)
370380
future2 = self.executor.submit(call2)
@@ -397,10 +407,10 @@ def wait_test():
397407
'this test assumes that future4 will be cancelled before it is '
398408
'queued to run - which might not be the case if '
399409
'ProcessPoolExecutor is too aggresive in scheduling futures')
400-
call1 = Call(manual_finish=True)
401-
call2 = Call(manual_finish=True)
402-
call3 = Call(manual_finish=True)
403-
call4 = Call(manual_finish=True)
410+
call1 = Call(self.Event, manual_finish=True)
411+
call2 = Call(self.Event, manual_finish=True)
412+
call3 = Call(self.Event, manual_finish=True)
413+
call4 = Call(self.Event, manual_finish=True)
404414
try:
405415
future1 = self.executor.submit(call1)
406416
future2 = self.executor.submit(call2)
@@ -432,8 +442,8 @@ def wait_test():
432442
pass
433443
call1.set_can()
434444

435-
call1 = Call(manual_finish=True)
436-
call2 = Call(manual_finish=True)
445+
call1 = Call(self.Event, manual_finish=True)
446+
call2 = Call(self.Event, manual_finish=True)
437447
try:
438448
future1 = self.executor.submit(call1)
439449
future2 = self.executor.submit(call2)
@@ -460,19 +470,11 @@ def wait_test():
460470
call2.close()
461471

462472

463-
class ThreadPoolWaitTests(WaitTests):
464-
def setUp(self):
465-
self.executor = futures.ThreadPoolExecutor(max_workers=1)
473+
class ThreadPoolWaitTests(ThreadPoolMixin, WaitTests):
474+
pass
466475

467-
def tearDown(self):
468-
self.executor.shutdown(wait=True)
469-
470-
class ProcessPoolWaitTests(WaitTests):
471-
def setUp(self):
472-
self.executor = futures.ProcessPoolExecutor(max_workers=1)
473-
474-
def tearDown(self):
475-
self.executor.shutdown(wait=True)
476+
class ProcessPoolWaitTests(ProcessPoolMixin, WaitTests):
477+
pass
476478

477479
class AsCompletedTests(unittest.TestCase):
478480
# TODO([email protected]): Should have a test with a non-zero timeout.
@@ -483,8 +485,8 @@ def wait_test():
483485
call1.set_can()
484486
call2.set_can()
485487

486-
call1 = Call(manual_finish=True)
487-
call2 = Call(manual_finish=True)
488+
call1 = Call(self.Event, manual_finish=True)
489+
call2 = Call(self.Event, manual_finish=True)
488490
try:
489491
future1 = self.executor.submit(call1)
490492
future2 = self.executor.submit(call2)
@@ -507,7 +509,7 @@ def wait_test():
507509
call2.close()
508510

509511
def test_zero_timeout(self):
510-
call1 = Call(manual_finish=True)
512+
call1 = Call(self.Event, manual_finish=True)
511513
try:
512514
future1 = self.executor.submit(call1)
513515
completed_futures = set()
@@ -529,19 +531,11 @@ def test_zero_timeout(self):
529531
finally:
530532
call1.close()
531533

532-
class ThreadPoolAsCompletedTests(AsCompletedTests):
533-
def setUp(self):
534-
self.executor = futures.ThreadPoolExecutor(max_workers=1)
535-
536-
def tearDown(self):
537-
self.executor.shutdown(wait=True)
534+
class ThreadPoolAsCompletedTests(ThreadPoolMixin, AsCompletedTests):
535+
pass
538536

539-
class ProcessPoolAsCompletedTests(AsCompletedTests):
540-
def setUp(self):
541-
self.executor = futures.ProcessPoolExecutor(max_workers=1)
542-
543-
def tearDown(self):
544-
self.executor.shutdown(wait=True)
537+
class ProcessPoolAsCompletedTests(ProcessPoolMixin, AsCompletedTests):
538+
pass
545539

546540
class ExecutorTest(unittest.TestCase):
547541
# Executor.shutdown() and context manager usage is tested by
@@ -567,7 +561,7 @@ def test_map_exception(self):
567561

568562
def test_map_timeout(self):
569563
results = []
570-
timeout_call = MapCall()
564+
timeout_call = MapCall(self.Event)
571565
try:
572566
try:
573567
for i in self.executor.map(timeout_call,
@@ -583,19 +577,11 @@ def test_map_timeout(self):
583577

584578
self.assertEqual([42, 42], results)
585579

586-
class ThreadPoolExecutorTest(ExecutorTest):
587-
def setUp(self):
588-
self.executor = futures.ThreadPoolExecutor(max_workers=1)
589-
590-
def tearDown(self):
591-
self.executor.shutdown(wait=True)
580+
class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest):
581+
pass
592582

593-
class ProcessPoolExecutorTest(ExecutorTest):
594-
def setUp(self):
595-
self.executor = futures.ProcessPoolExecutor(max_workers=1)
596-
597-
def tearDown(self):
598-
self.executor.shutdown(wait=True)
583+
class ProcessPoolExecutorTest(ProcessPoolMixin, ExecutorTest):
584+
pass
599585

600586
class FutureTests(unittest.TestCase):
601587
def test_done_callback_with_result(self):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ Core and Builtins
2020
Library
2121
-------
2222

23+
- Issue #10798: Reject supporting concurrent.futures if the system has too
24+
few POSIX semaphores.
25+
2326
- Issue #10807: Remove base64, bz2, hex, quopri, rot13, uu and zlib codecs from
2427
the codec aliases. They are still accessible via codecs.lookup().
2528

0 commit comments

Comments
 (0)