Esempio n. 1
0
class ThreadPoolExecutor(_base.Executor):

    # Used to assign unique thread names when thread_name_prefix is not supplied.
    _counter = itertools.count().__next__

    def __init__(self, max_workers=None, thread_name_prefix='',
                 initializer=None, initargs=()):
        """Initializes a new ThreadPoolExecutor instance.

        Args:
            max_workers: The maximum number of threads that can be used to
                execute the given calls.
            thread_name_prefix: An optional name prefix to give our threads.
            initializer: A callable used to initialize worker threads.
            initargs: A tuple of arguments to pass to the initializer.
        """
        if max_workers is None:
            # ThreadPoolExecutor is often used to:
            # * CPU bound task which releases GIL
            # * I/O bound task (which releases GIL, of course)
            #
            # We use cpu_count + 4 for both types of tasks.
            # But we limit it to 32 to avoid consuming surprisingly large resource
            # on many core machine.
            max_workers = min(32, (os.cpu_count() or 1) + 4)
        if max_workers <= 0:
            raise ValueError("max_workers must be greater than 0")

        if initializer is not None and not callable(initializer):
            raise TypeError("initializer must be a callable")

        self._max_workers = max_workers
        self._work_queue = queue.SimpleQueue()
        self._idle_semaphore = threading.Semaphore(0)
        self._threads = set()
        self._broken = False
        self._shutdown = False
        self._shutdown_lock = threading.Lock()
        self._thread_name_prefix = (thread_name_prefix or
                                    ("ThreadPoolExecutor-%d" % self._counter()))
        self._initializer = initializer
        self._initargs = initargs

    def submit(self, fn, /, *args, **kwargs):
        with self._shutdown_lock, _global_shutdown_lock:
            if self._broken:
                raise BrokenThreadPool(self._broken)

            if self._shutdown:
                raise RuntimeError('cannot schedule new futures after shutdown')
            if _shutdown:
                raise RuntimeError('cannot schedule new futures after '
                                   'interpreter shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._work_queue.put(w)
            self._adjust_thread_count()
            return f
Esempio n. 2
0
    def submit(self, fn, *args, **kwargs):
        """Attempts to submit the work item.

    A runtime error is raised if the pool has been shutdown.
    """
        future = _base.Future()
        work_item = _WorkItem(future, fn, args, kwargs)
        try:
            # Keep trying to get an idle worker from the queue until we find one
            # that accepts the work.
            while not self._idle_worker_queue.get(
                    block=False).accepted_work(work_item):
                pass
            return future
        except queue.Empty:
            with self._lock:
                if self._shutdown:
                    raise RuntimeError(
                        'Cannot schedule new tasks after thread pool '
                        'has been shutdown.')

                worker = _Worker(self._idle_worker_queue,
                                 self._permitted_thread_age_in_seconds,
                                 work_item)
                worker.daemon = True
                worker.start()
                self._workers.add(worker)
                return future
Esempio n. 3
0
 def __init__(self, fn, sensor_id):
     self.fn = fn
     if isinstance(sensor_id, Sensor):
         self.sensor_id = sensor_id.id
     else:
         self.sensor_id = sensor_id
     self.future = _base.Future()
Esempio n. 4
0
    def submit(self, fn, *args, **kwargs):
        """Attempts to submit the work item.

    A runtime error is raised if the pool has been shutdown.
    """
        future = _base.Future()
        work_item = _WorkItem(future, fn, args, kwargs)
        with self._lock:
            if self._shutdown:
                raise RuntimeError(
                    'Cannot schedule new tasks after thread pool has been shutdown.'
                )
            try:
                self._idle_worker_queue.get(block=False).assign_work(work_item)

                # If we have more idle threads then the max allowed, shutdown a thread.
                if self._idle_worker_queue.qsize() > self._max_idle_threads:
                    try:
                        self._idle_worker_queue.get(block=False).shutdown()
                    except queue.Empty:
                        pass
            except queue.Empty:
                worker = _Worker(self._idle_worker_queue, work_item)
                worker.daemon = True
                worker.start()
                self._workers.add(worker)
        return future
Esempio n. 5
0
    def submit(self, cmd, *args, **kwargs):
        """Submits a command to be executed as Popen(cmd.format(*args), **kwargs)

        Schedules the command to delegate for `subprocess.Popen()` and return a
        Future instance representing the execution of the callable.
        there have some arguments different with Popen are:
            - stdout, stderr always `PIPE`. (not allowed re-assign)
            - shell be changed to True for default.
            - universal_newlines be changed to True for default.
            - position arguments need to use keyword arguments for propagate.

        Returns:
            A Future representing the given call.
        """
        with self._shutdown_lock:
            if self._shutdown:
                raise RuntimeError(
                    "cannot schedule new futures after shutdown.")

            f = _base.Future()
            w = _WorkItem(f, cmd.format(*args), kwargs)

            self._work_queue.put(w)
            self._start_management_thread()
            return f
Esempio n. 6
0
    def submit(*args, **kwargs):
        if len(args) >= 2:
            self, fn, *args = args
        elif not args:
            raise TypeError(
                "descriptor 'submit' of 'ThreadPoolExecutor' object "
                "needs an argument")
        elif 'fn' in kwargs:
            fn = kwargs.pop('fn')
            self, *args = args
        else:
            raise TypeError('submit expected at least 1 positional argument, '
                            'got %d' % (len(args) - 1))

        with self._shutdown_lock:
            if self._broken:
                raise BrokenThreadPool(self._broken)

            if self._shutdown:
                raise RuntimeError(
                    'cannot schedule new futures after shutdown')
            if _shutdown:
                raise RuntimeError('cannot schedule new futures after '
                                   'interpreter shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._work_queue.put(w)
            self._adjust_thread_count()
            return f
Esempio n. 7
0
File: io.py Progetto: speleo3/conda
    def submit(self, fn, *args, **kwargs):
        """
        This is an exact reimplementation of the `submit()` method on the parent class, except
        with an added `try/except` around `self._adjust_thread_count()`.  So long as there is at
        least one living thread, this thread pool will not throw an exception if threads cannot
        be expanded to `max_workers`.

        In the implementation, we use "protected" attributes from concurrent.futures (`_base`
        and `_WorkItem`). Consider vendoring the whole concurrent.futures library
        as an alternative to these protected imports.

        https://github.com/agronholm/pythonfutures/blob/3.2.0/concurrent/futures/thread.py#L121-L131  # NOQA
        https://github.com/python/cpython/blob/v3.6.4/Lib/concurrent/futures/thread.py#L114-L124
        """
        with self._shutdown_lock:
            if self._shutdown:
                raise RuntimeError(
                    'cannot schedule new futures after shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._work_queue.put(w)
            try:
                self._adjust_thread_count()
            except RuntimeError:
                # RuntimeError: can't start new thread
                # See https://github.com/conda/conda/issues/6624
                if len(self._threads) > 0:
                    # It's ok to not be able to start new threads if we already have at least
                    # one thread alive.
                    pass
                else:
                    raise
            return f
Esempio n. 8
0
def submit(fn, *args, **kwargs):
    '''Submit a function to be run on the executor (internal)

    Args:
        fn (callable): function to call
        args (tuple): args to pass to function
        kwargs (dict): kwargs to pass to function
    '''
    if _EXECUTOR is None:
        raise RuntimeError('Already stopped!')
    self = _EXECUTOR
    with self._shutdown_lock:
        if hasattr(self, '_broken') and self._broken:
            raise BrokenThreadPool(self._broken)

        if hasattr(self, '_shutdown') and self._shutdown:
            raise RuntimeError('cannot schedule new futures after shutdown')
        if cft._shutdown:
            raise RuntimeError('cannot schedule new futures after'
                               'interpreter shutdown')

        f = _base.Future()
        w = _WorkItem(f, fn, args, kwargs)

        self._work_queue.put(w)
        self._adjust_thread_count()
        return f
Esempio n. 9
0
    def submit(*args, **kwargs):
        if len(args) >= 2:
            self, fn, *args = args
        elif not args:
            raise TypeError(
                "descriptor 'submit' of 'ProcessPoolExecutor' object "
                "needs an argument")
        elif 'fn' in kwargs:
            fn = kwargs.pop('fn')
            self, *args = args
        else:
            raise TypeError('submit expected at least 1 positional argument, '
                            'got %d' % (len(args) - 1))

        with self._shutdown_lock:
            if self._broken:
                raise BrokenProcessPool(self._broken)
            if self._shutdown_thread:
                raise RuntimeError(
                    'cannot schedule new futures after shutdown')
            if _global_shutdown:
                raise RuntimeError('cannot schedule new futures after '
                                   'interpreter shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._pending_work_items[self._queue_count] = w
            self._work_ids.put(self._queue_count)
            self._queue_count += 1
            # Wake up queue management thread
            self._queue_management_thread_wakeup.wakeup()

            self._start_queue_management_thread()
            return f
Esempio n. 10
0
    def submit(*args, **kwargs):
        if len(args) >= 2:
            self, fn, *args = args
        elif not args:
            raise TypeError("descriptor 'submit' of 'ErDummyThreadPool' object "
                            "needs an argument")
        elif 'fn' in kwargs:
            fn = kwargs.pop('fn')
            self, *args = args
        else:
            raise TypeError('submit expected at least 1 positional argument, '
                            'got %d' % (len(args)-1))

        with self._shutdown_lock:
            if self._shutdown:
                raise RuntimeError('cannot schedule new futures after shutdown')
            if _shutdown:
                raise RuntimeError('cannot schedule new futures after '
                                   'interpreter shutdown')
            self.increase_thread_count()
            thread_name = f'{self._thread_name_prefix or self}_{self._num_threads}'
            f = _base.Future()
            w = _ErWorkItem(f, fn, args, kwargs)
            t = threading.Thread(name=thread_name, target=w.run, kwargs={"on_join": self.decrease_thread_count})
            t.start()

            return f
Esempio n. 11
0
    def __init__(self, fn, sensor_id):
        self.fn = fn
        if isinstance(sensor_id, Device):
            self.sensor_id = sensor_id.deviceId
        else:
            self.sensor_id = int(sensor_id)

        self.future = _base.Future()
Esempio n. 12
0
 def submit(self, fn, *args, **kwargs):
     with self._shutdown_lock:
         if self._shutdown:
             raise RuntimeError(
                 'cannot schedule new futures after shutdown')
         f = _base.Future()
         w = _WorkItem(f, fn, args, kwargs)
         self._work_queue.put(w)
         self._adjust_thread_count()
         return f
Esempio n. 13
0
    def submit(self, fn, *args, **kwargs):
        with self._lock:
            if not self._running:
                raise RuntimeError('task pool is not running')

            future = futurebase.Future()
            task = Task(future, fn, args, kwargs)

            self._queue.put(task)

            return future
Esempio n. 14
0
    def post(self, message):
        with self._shutdown_lock:
            if self._shutdown:
                raise RuntimeError(
                    'cannot schedule new futures after shutdown')

            f = _base.Future()
            w = _WorkItem(f, message)

            self._work_queue.put(w)
            self._initialize_actor()
            return f
Esempio n. 15
0
    def __init__(self, fn, sensor_id):
        """
        Initialize the WorkItem.

        Args:
            fn (func): The function to be called to do the actual work.
            sensor_id (object): The sensor ID or Sensor object the work item is directed for.
        """
        self.fn = fn
        if isinstance(sensor_id, Sensor):
            self.sensor_id = sensor_id.id
        else:
            self.sensor_id = int(sensor_id)

        self.future = _base.Future()
Esempio n. 16
0
    def submit(self, fn, *args, **kwargs):
        #submit函数会立刻返回
        with self._shutdown_lock:
            if self._shutdown:
                raise RuntimeError(
                    'cannot schedule new futures after shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            #work queue被所有线程共享, 里面放入 _WorkItem对象
            #_WotkItem对象有一个 属性是Future的实例, 用它来储存返回结果, 并且该实例会被submmit返回
            self._work_queue.put(w)
            self._adjust_thread_count()
            return f
Esempio n. 17
0
    def submit(self, fn, *args, **kwargs):
        with self._shutdown_lock:
            if self._shutdown_thread:
                raise RuntimeError('cannot schedule new futures after shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._pending_work_items[self._queue_count] = w
            self._work_ids.put(self._queue_count)
            self._queue_count += 1

            self._start_queue_management_thread()
            self._adjust_process_count()
            return f
Esempio n. 18
0
    def __init__(self, fn, device_id):
        """
        Initialize the WorkItem.

        Args:
            fn (func): The function to be called to do the actual work.
            device_id (object): The device ID or Device object the work item is directed for.
        """
        self.fn = fn
        if isinstance(device_id, Device):
            self.device_id = device_id.id
        else:
            self.device_id = int(device_id)

        self.future = _base.Future()
Esempio n. 19
0
 def submit(self, fn, *args, **kwargs):
     with self._shutdown_lock:
         if self._broken:
             raise BrokenProcessPool(
                 'A child process terminated abruptly, the process pool is not usable anymore'
             )
         if self._shutdown_thread:
             raise RuntimeError(
                 'cannot schedule new futures after shutdown')
         f = _base.Future()
         w = _WorkItem(f, fn, args, kwargs)
         self._pending_work_items[self._queue_count] = w
         self._work_ids.put(self._queue_count)
         self._result_queue.put(None)
         self._start_queue_management_thread()
         return f
Esempio n. 20
0
    def submit(self, fn, *args, **kwargs):
        future = _base.Future()
        work_item = WorkItem(future, fn, args, kwargs)

        self.work_queue.put(work_item)

        start_worker = False
        with self.workers_busy_lock:
            if not any([not worker.busy for worker in self.workers]):
                logger.trace("Starting new worker in namespace %r because there are no free workers",
                             self.thread_name_prefix)
                start_worker = True
        if start_worker:
            self._start_worker()

        return future
Esempio n. 21
0
    def submit(self, fn, *args, **kwargs):
        with self._shutdown_lock:
            if self._broken:
                raise BrokenProcessPool(self._broken)
            if self._shutdown_thread:
                raise RuntimeError('cannot schedule new futures after shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._pending_work_items[self._queue_count] = w
            self._work_ids.put(self._queue_count)
            self._queue_count += 1
            # Wake up queue management thread
            self._result_queue.put(None)

            self._start_queue_management_thread()
            return f
Esempio n. 22
0
    def submit(self, fn, *args, **kwargs):  # 每创建一个submit,都是会创建一个新的线程
        with self._shutdown_lock:  # 确实是每一个都上锁操作
            if self._broken:
                raise BrokenThreadPool(self._broken)

            if self._shutdown:
                raise RuntimeError(
                    'cannot schedule new futures after shutdown')
            if _shutdown:
                raise RuntimeError('cannot schedule new futures after '
                                   'interpreter shutdown')

            f = _base.Future()  # 创建一个未来对象
            w = _WorkItem(f, fn, args, kwargs)  # 实际工作对象
            # 所谓的submit其实就是用目标函数注册一个未来对象,用此未来对象申请一个工作调度对象,然后将此工作对象推到队列中就完事了
            self._work_queue.put(w)  # 每一次submit都会将实际工作对象推入队列中
            self._adjust_thread_count()
            return f  # 返回注册函数申请的未来对象
Esempio n. 23
0
def submit(fn, *args, **kwargs):
    if _EXECUTOR is None:
        raise RuntimeError('Already stopped!')
    self = _EXECUTOR
    with self._shutdown_lock:
        if self._broken:
            raise BrokenThreadPool(self._broken)

        if self._shutdown:
            raise RuntimeError('cannot schedule new futures after shutdown')
        if cft._shutdown:
            raise RuntimeError('cannot schedule new futures after'
                               'interpreter shutdown')

        f = _base.Future()
        w = _WorkItem(f, fn, args, kwargs)

        self._work_queue.put(w)
        self._adjust_thread_count()
        return f
Esempio n. 24
0
    def submit(self, fn, *args, **kwargs):
        with self._shutdown_lock:
            if self._broken:
                raise BrokenProcessPool('A child process terminated '
                    'abruptly, the process pool is not usable anymore')
            if self._shutdown_thread:
                raise RuntimeError('cannot schedule new futures after shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._pending_work_items[self._queue_count] = w
            self._work_ids.put(self._queue_count)
            self._queue_count += 1
            # Wake up queue management thread
            self._result_queue.put(None)
            # 这个None其实只是为了让workder thread的ready = wait([reader] + sentinels)
            # 的reader有东西,可以往下运行,执行到下一次循环,调用_add_call_item_to_queue
            # 不会做其它任何事
            self._start_queue_management_thread()
            return f
Esempio n. 25
0
    def post(self, message):
        with self._shutdown_lock:
            if self._broken:
                raise process.BrokenProcessPool(
                    'A child process terminated '
                    'abruptly, the process pool is not usable anymore')
            if self._shutdown_thread:
                raise RuntimeError(
                    'cannot schedule new futures after shutdown')

            f = _base.Future()
            w = _WorkItem(f, message)

            self._pending_work_items[self._queue_count] = w
            self._work_ids.put(self._queue_count)
            self._queue_count += 1
            # Wake up queue management thread
            self._result_queue.put(None)

            self._start_queue_management_thread()
            return f
Esempio n. 26
0
    def submit(self, fn, *args, **kwargs):
        with self._shutdown_lock:
            if self._broken:
                raise BrokenThreadPool(self._broken)

            if self._shutdown:
                raise RuntimeError('cannot schedule new futures after shutdown')
            if _shutdown:
                raise RuntimeError('cannot schedule new futures after '
                                   'interpreter shutdown')

            priority = kwargs.get('priority', random.randint(0, sys.maxsize-1))
            if 'priority' in kwargs:
                del kwargs['priority']

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._work_queue.put((priority, w), block=False)
            self._adjust_thread_count()
            return f
    def submit(*args, **kwargs):
        if len(args) >= 2:
            self, fn, *args = args
        elif not args:
            raise TypeError(
                "descriptor 'submit' of 'ThreadPoolExecutor' object "
                "needs an argument")
        elif 'fn' in kwargs:
            fn = kwargs.pop('fn')
            self, *args = args
            import warnings
            warnings.warn("Passing 'fn' as keyword argument is deprecated",
                          DeprecationWarning,
                          stacklevel=2)
        else:
            raise TypeError('submit expected at least 1 positional argument, '
                            'got %d' % (len(args) - 1))

        with self._shutdown_lock, _global_shutdown_lock:
            if self._broken:
                raise thread.BrokenThreadPool(self._broken)

            if self._shutdown:
                raise RuntimeError(
                    'cannot schedule new futures after shutdown')
            if _shutdown:
                raise RuntimeError('cannot schedule new futures after '
                                   'interpreter shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._work_queue.put(w)
            with self._job_counter_lock:
                self._job_counter += 1
            self._adjust_thread_count()
            return f
    def submit(self, fn, *args, **kwargs):
        with self._shutdown_lock:
            if self._broken:
                raise BrokenThreadPool(self._broken)

            if self._shutdown:
                raise RuntimeError('cannot schedule new futures after shutdown')
            if _shutdown:
                raise RuntimeError('cannot schedule new futures after '
                                   'interpreter shutdown')

            priority = kwargs.get('priority', random.randint(0, 1000000))
            if priority == 0:
                priority = random.randint(1, 100)
            eplison = random.uniform(0,0.01) * priority
            if 'priority' in kwargs:
                del kwargs['priority']

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._work_queue.put((-float(priority + eplison), w), block=False)
            self._adjust_thread_count()
            return f
Esempio n. 29
0
 def _shutdown_trigger(self):
     return base.Future()
Esempio n. 30
0
class ProcessPoolExecutor(_base.Executor):
    def __init__(self,
                 max_workers=None,
                 mp_context=None,
                 initializer=None,
                 initargs=()):
        """Initializes a new ProcessPoolExecutor instance.

        Args:
            max_workers: The maximum number of processes that can be used to
                execute the given calls. If None or not given then as many
                worker processes will be created as the machine has processors.
            mp_context: A multiprocessing context to launch the workers. This
                object should provide SimpleQueue, Queue and Process.
            initializer: A callable used to initialize worker processes.
            initargs: A tuple of arguments to pass to the initializer.
        """
        _check_system_limits()

        if max_workers is None:
            self._max_workers = os.cpu_count() or 1
            if sys.platform == 'win32':
                self._max_workers = min(_MAX_WINDOWS_WORKERS,
                                        self._max_workers)
        else:
            if max_workers <= 0:
                raise ValueError("max_workers must be greater than 0")
            elif (sys.platform == 'win32'
                  and max_workers > _MAX_WINDOWS_WORKERS):
                raise ValueError(
                    f"max_workers must be <= {_MAX_WINDOWS_WORKERS}")

            self._max_workers = max_workers

        if mp_context is None:
            mp_context = mp.get_context()
        self._mp_context = mp_context

        if initializer is not None and not callable(initializer):
            raise TypeError("initializer must be a callable")
        self._initializer = initializer
        self._initargs = initargs

        # Management thread
        self._executor_manager_thread = None

        # Map of pids to processes
        self._processes = {}

        # Shutdown is a two-step process.
        self._shutdown_thread = False
        self._shutdown_lock = threading.Lock()
        self._idle_worker_semaphore = threading.Semaphore(0)
        self._broken = False
        self._queue_count = 0
        self._pending_work_items = {}
        self._cancel_pending_futures = False

        # _ThreadWakeup is a communication channel used to interrupt the wait
        # of the main loop of executor_manager_thread from another thread (e.g.
        # when calling executor.submit or executor.shutdown). We do not use the
        # _result_queue to send wakeup signals to the executor_manager_thread
        # as it could result in a deadlock if a worker process dies with the
        # _result_queue write lock still acquired.
        #
        # _shutdown_lock must be locked to access _ThreadWakeup.
        self._executor_manager_thread_wakeup = _ThreadWakeup()

        # Create communication channels for the executor
        # Make the call queue slightly larger than the number of processes to
        # prevent the worker processes from idling. But don't make it too big
        # because futures in the call queue cannot be cancelled.
        queue_size = self._max_workers + EXTRA_QUEUED_CALLS
        self._call_queue = _SafeQueue(
            max_size=queue_size,
            ctx=self._mp_context,
            pending_work_items=self._pending_work_items,
            shutdown_lock=self._shutdown_lock,
            thread_wakeup=self._executor_manager_thread_wakeup)
        # Killed worker processes can produce spurious "broken pipe"
        # tracebacks in the queue's own worker thread. But we detect killed
        # processes anyway, so silence the tracebacks.
        self._call_queue._ignore_epipe = True
        self._result_queue = mp_context.SimpleQueue()
        self._work_ids = queue.Queue()

    def _start_executor_manager_thread(self):
        if self._executor_manager_thread is None:
            # Start the processes so that their sentinels are known.
            self._executor_manager_thread = _ExecutorManagerThread(self)
            self._executor_manager_thread.start()
            _threads_wakeups[self._executor_manager_thread] = \
                self._executor_manager_thread_wakeup

    def _adjust_process_count(self):
        # if there's an idle process, we don't need to spawn a new one.
        if self._idle_worker_semaphore.acquire(blocking=False):
            return

        process_count = len(self._processes)
        if process_count < self._max_workers:
            p = self._mp_context.Process(
                target=_process_worker,
                args=(self._call_queue, self._result_queue, self._initializer,
                      self._initargs))
            p.start()
            self._processes[p.pid] = p

    def submit(self, fn, /, *args, **kwargs):
        with self._shutdown_lock:
            if self._broken:
                raise BrokenProcessPool(self._broken)
            if self._shutdown_thread:
                raise RuntimeError(
                    'cannot schedule new futures after shutdown')
            if _global_shutdown:
                raise RuntimeError('cannot schedule new futures after '
                                   'interpreter shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._pending_work_items[self._queue_count] = w
            self._work_ids.put(self._queue_count)
            self._queue_count += 1
            # Wake up queue management thread
            self._executor_manager_thread_wakeup.wakeup()

            self._adjust_process_count()
            self._start_executor_manager_thread()
            return f