1# Copyright 2020, 2021 PaGMO development team
2#
3# This file is part of the pygmo library.
4#
5# This Source Code Form is subject to the terms of the Mozilla
6# Public License v. 2.0. If a copy of the MPL was not distributed
7# with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
9
10def _get_spawn_context():
11    # Small utlity to get a context that will use the 'spawn' method to
12    # create new processes with the multiprocessing module. We want to enforce
13    # a uniform way of creating new processes across platforms in
14    # order to prevent users from implicitly relying on platform-specific
15    # behaviour (e.g., fork()), only to discover later that their
16    # code is not portable across platforms. See:
17    # https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods
18
19    import multiprocessing as mp
20
21    return mp.get_context('spawn')
22
23
24class _temp_disable_sigint(object):
25    # A small helper context class to disable CTRL+C temporarily.
26
27    def __enter__(self):
28        import signal
29        # Store the previous sigint handler and assign the new sig handler
30        # (i.e., ignore SIGINT).
31        self._prev_signal = signal.signal(signal.SIGINT, signal.SIG_IGN)
32
33    def __exit__(self, type, value, traceback):
34        import signal
35        # Restore the previous sighandler.
36        signal.signal(signal.SIGINT, self._prev_signal)
37
38
39def _make_pool(processes):
40    # A small factory function to create a process pool.
41    # It accomplishes the tasks of selecting the correct method for
42    # starting the processes ("spawn") and making sure that the
43    # created processes will ignore the SIGINT signal (this prevents
44    # troubles when the user issues an interruption with ctrl+c from
45    # the main process).
46
47    if processes is not None and not isinstance(processes, int):
48        raise TypeError("The 'processes' argument must be None or an int")
49
50    if processes is not None and processes <= 0:
51        raise ValueError(
52            "The 'processes' argument, if not None, must be strictly positive")
53
54    # Get the context for spawning the process.
55    mp_ctx = _get_spawn_context()
56
57    # NOTE: we temporarily disable sigint while creating the pool.
58    # This ensures that the processes created in the pool will ignore
59    # interruptions issued via ctrl+c (only the main process will
60    # be affected by them).
61    with _temp_disable_sigint():
62        pool = mp_ctx.Pool(processes=processes)
63
64    pool_size = mp_ctx.cpu_count() if processes is None else processes
65
66    # Return the created pool and its size.
67    return pool, pool_size
68