Skip to content

-shared: auto-call GC_allow_register_threads() from a library constructor #27178

@eptx

Description

@eptx

Reproduced on V 0.5.1 (commit 99f141f in upstream master);
verified no relevant commits land between that and current
master b6dfae5. OS: macOS 26.4.1 ARM64.

Problem

A V -shared library built with the default Boehm GC is unusable from
host-spawned threads (Rust cargo workers, C# tasks, Java JNI threads)
because libgc aborts with "Collecting from unknown thread" whenever a
collection is triggered from a thread libgc has not been told about.

To register host threads, the binding must:

  1. Call GC_allow_register_threads() once at module load (process-
    level).
  2. Call GC_register_my_thread(&stack_base) per thread before that
    thread enters any V code.

Step 2 is inherently per-binding (the binding knows when it spawns a
thread). Step 1 is process-level and could be done by V itself.

Proposed fix

When emitting a -shared library and the default GC is Boehm, emit a
constructor that runs at dlopen time:

__attribute__((constructor))
static void _v_shared_lib_init(void) {
    GC_allow_register_threads();
}

That is the entire change. After it, host bindings only need the
per-thread call, which they need anyway.

Downstream evidence

CX currently exports its own cx_init symbol whose entire body is

void cx_gc_allow_register_threads(void) {
    GC_allow_register_threads();
}

Every binding (Rust, C#, Java, …) is required to call cx_init() at
module load, and every binding's onboarding doc has a "remember to
call cx_init" footgun. With this V change, the cx_init export goes
away.

Why not GC-side?

bdwgc deliberately requires the host to opt in to thread-registration,
because for single-threaded users the cost of the lookup tables is
real. V already knows when it's building a multi-threaded -shared
library; emitting the constructor is the right layer.

Note

You can use the 👍 reaction to increase the issue's priority for developers.

Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions