def _submit_task(self, func, task, *args, **kwargs):
        """Submit a function to run the given task (with given args/kwargs).

        NOTE(harlowja): Adjust all events to be proxies instead since we want
        those callbacks to be activated in this process, not in the child,
        also since typically callbacks are functors (or callables) we can
        not pickle those in the first place...

        To make sure people understand how this works, the following is a
        lengthy description of what is going on here, read at will:

        So to ensure that we are proxying task triggered events that occur
        in the executed subprocess (which will be created and used by the
        thing using the multiprocessing based executor) we need to establish
        a link between that process and this process that ensures that when a
        event is triggered in that task in that process that a corresponding
        event is triggered on the original task that was requested to be ran
        in this process.

        To accomplish this we have to create a copy of the task (without
        any listeners) and then reattach a new set of listeners that will
        now instead of calling the desired listeners just place messages
        for this process (a dispatcher thread that is created in this class)
        to dispatch to the original task (using a common queue + per task
        sender identity/target that is used and associated to know which task
        to proxy back too, since it is possible that there many be *many*
        subprocess running at the same time, each running a different task
        and using the same common queue to submit messages back to).

        Once the subprocess task has finished execution, the executor will
        then trigger a callback that will remove the task + target from the
        dispatcher (which will stop any further proxying back to the original
        task).
        """
        progress_callback = kwargs.pop('progress_callback', None)
        clone = task.copy(retain_listeners=False)
        identity = uuidutils.generate_uuid()
        target = _Target(task, self._manager.Event(), identity)
        channel = _Channel(self._queue, identity)
        self._rebind_task(task, clone, channel,
                          progress_callback=progress_callback)

        def register():
            if progress_callback is not None:
                task.notifier.register(_UPDATE_PROGRESS, progress_callback)
            self._dispatcher.register(identity, target)

        def deregister():
            if progress_callback is not None:
                task.notifier.deregister(_UPDATE_PROGRESS, progress_callback)
            self._dispatcher.deregister(identity)

        register()
        work = _WaitWorkItem(channel, target.barrier,
                             func, clone, *args, **kwargs)
        try:
            fut = self._executor.submit(work)
        except __HOLE__:
            with excutils.save_and_reraise_exception():
                deregister()

        fut.atom = task
        fut.add_done_callback(lambda fut: deregister())
        return fut
dataset/ETHPy150Open openstack/taskflow/taskflow/engines/action_engine/executor.py/ParallelProcessTaskExecutor._submit_task