Skip to content

run_process ignores thread context and spawns from threadpool #3360

Open

Description

@tik-stbuehler

Hi,

run_process uses trio.to_thread.run_sync to spawn the subprocess; this means that the spawned process will inherit various context from a different thread. In my case this means it spawns in the wrong (network) namespace (see setns(2) - "reassociate thread with a namespace").

This might affect other things as well (pthreads(7) lists (among other things) capabilities and CPU affinity).

  1. the docs should mention this behavior
  2. it would be nice to have a flag to "spawn from current thread"

I'm also open to good suggestions how to work around this problem :)
(I might try prefixing the target command with ["/usr/bin/nsenter", "--target", threading.get_native_id(), "-n", "--"], or copy the trio run_process/open_process functions).

cheers,
Stefan

Activity

  1. A5rocks commented on Nov 26, 2025

    @A5rocks
    Contributor

    Looking at the docs, it sounds like we would want to use clone(2) to get a thread for sync stuff?

    Not all of the attributes that can be shared when a new thread is created using clone(2) can be changed using setns().

    Probably also the thread pool has some capacity limiter (I forgot!) so any change would have to beware... but maybe we can just call clone(2) if it's available to make a thread? I haven't looked at its manpage.

    (Disclaimer: I don't know my way around the syscalls available)

  2. tik-stbuehler commented on Nov 27, 2025

    @tik-stbuehler
    Author

    Looking at the docs, it sounds like we would want to use clone(2) to get a thread for sync stuff?

    I strongly recommend against (even temporarily) cloning a new thread for every subprocess, and that would be the only way to make sure the subprocess inherits the current context.

    Actually, in my case a worker thread(pool) per "normal thread"/"trio loop" would do, because changing the namespace is the first thing I do, i.e. before calling trio.run; but I have multiple threads doing so with different namespaces. See code example at the bottom.

    Not all of the attributes that can be shared when a new thread is created using clone(2) can be changed using setns().

    No; I think this just means "not every CLONE_* flag is a namespace flag" - i.e. not every CLONE_* flag can be used with setns() (but I think all CLONE_NEW* flags actually can be used).

    Probably also the thread pool has some capacity limiter (I forgot!) so any change would have to beware... but maybe we can just call clone(2) if it's available to make a thread? I haven't looked at its manpage.

    Any high-level capacity limiter of a thread pool is unrelated to clone(2) and potential OS resource limits. But if you hit such an OS limit creating a new process is very likely to fail too.


    Attaching possible workaround for using a (thread-)local threadpool where needed (call use_local_thread_pool at least once in such threads):

    from __future__ import annotations
    
    from trio._core._thread_cache import ThreadCache, THREAD_CACHE as ORIG_CACHE
    import trio._core._thread_cache
    import threading
    import typing
    
    if typing.TYPE_CHECKING:
        import outcome
    
    
    _THREAD_LOCALS = threading.local()
    
    
    def use_local_thread_pool() -> None:
        # activate workaround for https://github.com/python-trio/trio/issues/3360
        if not hasattr(_THREAD_LOCALS, "pool"):
            _THREAD_LOCALS.pool = ThreadCache()
    
    
    class _StubCache:
        _idle_workers = ORIG_CACHE._idle_workers
    
        def start_thread_soon(
            self,
            fn: typing.Callable[[], trio._core._thread_cache.RetT],
            deliver: typing.Callable[[outcome.Outcome[trio._core._thread_cache.RetT]], object],
            name: str | None = None,
        ) -> None:
            if hasattr(_THREAD_LOCALS, "pool"):
                _THREAD_LOCALS.pool.start_thread_soon(fn, deliver, name)
            else:
                ORIG_CACHE.start_thread_soon(fn, deliver, name)
    
    
    trio._core._thread_cache.THREAD_CACHE = _StubCache()  # type: ignore
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions