Skip to content

Can we support stackful coroutines / fibers? #602

Open

Description

@RalfJung

Rust has no official support for using stackful coroutines (such as the fiber APIs on Windows, or doing the same with inline asm stack switching on Linux) -- but people do it anyway so there's clearly a demand (Cc @Amanieu). However, this means that the addresses of TLS variables can change basically any time, which is unsound as LLVM assumes the opposite (llvm/llvm-project#98479). Cc @nikic

So, right now doing stack switching or using the Windows fiber API in Rust is pretty much UB (at least if the code also uses thread-local variables, which std does so that's basically a given). There's little we can do about that until LLVM has a way of avoiding that UB, but it seems worth tracking this on the Rust side and maybe brainstorming ideas about how stackful coroutines could be supported without pessimizing all optimizations everywhere: the fundamental issue is that any function call might change the thread this function runs on, so all thread-local variables have to be re-loaded, but most function calls don't actually do that so usually this is a pointless pessimization.

Activity

  1. CAD97 commented on Jan 29, 2026

    @CAD97
    Contributor

    FWIW, any function which could potentially execute a fiber switch probably needs to be considered unsafe, as I think it's unsound to do a fiber switch in combination with the LocalKey::with API:

    STATIC.with(|value: &_| {
        unsafe { may_suspend_fiber() };
        // original hardware thread may have exited
        Retag(&*value);
    });

    For the above, which explicitly holds a TLS slot's value's reference across a suspend point, to be sound, it requires the fiber runtime to guarantee that any thread which executes a fiber switch does not destroy TLS before all fibers have been terminated. (I do not know if this is a standard guarantee.)


    If we were to provide a compilation switch that ensures all TLS loads in the source are non-reorderable events, its potentially worth noting that the overhead in typical Rust code may be slightly lower than in typical C or C++ code, since the LocalKey API makes TLS fetches more visible than the thread_local storage-class specifier in C and C++, thus theoretically resulting in fewer source locations which directly use the thread local place when long-scoped LocalKey::with callbacks are used. Making every access through LocalKey emit a fresh TLS lookup seems less "surprising" than every mention of a #[thread_local] place being a forced TLS access.

  2. RalfJung commented on Jan 29, 2026

    @RalfJung
    MemberAuthor

    I don't think we'd make this a compilation switch -- that would basically fork the language, which sounds bad. Either we do this via some form of annotation on the relevant functions, or we globally always assume any function may switch the fiber to a different thread.

  3. Diggsey commented on Jan 29, 2026

    @Diggsey

    Windows provides FsAlloc/FsFree/etc functions which mirror TlsAlloc/TlsFree/etc but are local to the fiber rather than the thread.

    So, I'm not saying we should do this, but one option would be to resurrect Rust's pluggable concurrency of old. Similar to how you can compile with panic=abort, maybe you can compile with concurrency=fiber and all thread-local stuff is instead fiber-local.

  4. RalfJung commented on Jan 30, 2026

    @RalfJung
    MemberAuthor

    It is generally considered unsound to write code that needs a particular panic runtime to avoid UB; I would apply the same standard for concurrency runtimes.


    @Amanieu mentioned that corosensei avoids the TLS issue by never switching fibers from one thread to another. Does that prevent all possible incompatibilities with LLVM or could there still be optimizations that break stuff? LocalKey::with should be fine in that case I think.

  5. Amanieu commented on Jan 30, 2026

    @Amanieu
    Member

    As far as I know, TLS access is the only potential issue with fibers in LLVM.

  6. Darksonn commented on Feb 24, 2026

    @Darksonn
    Member

    I guess this also affects Tokio's loom testing framework, which relies on inline asm stack switching.

  7. added
    A-missingThere's something missing from the AM that prevents this from being possible.
    on Apr 16, 2026
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

    A-missingThere's something missing from the AM that prevents this from being possible.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions