vxx2 / vlib / v / tests / shared_library_boehm_register_threads_test.v
47 lines · 45 sloc · 2.04 KB · 4ba2b1c8d70b0d9cd2cd80cf6dadd8dfb62d3009
Raw
1import os
2import rand
3
4const vexe = @VEXE
5
6// Regression test for vlang/v#27178: when a V `-shared` library is built
7// with `-gc boehm`, the generated C must call `GC_allow_register_threads()`
8// during library init. Without it, host-spawned threads (Rust workers,
9// JNI threads, C# tasks, ...) that later enter V code cause libgc to abort
10// with `"Collecting from unknown thread"` on the first collection.
11fn test_shared_library_boehm_emits_allow_register_threads() {
12 workdir := os.join_path(os.vtmp_dir(), 'v_shared_gc_threads_${rand.ulid()}')
13 os.mkdir_all(workdir) or { panic(err) }
14 defer {
15 os.rmdir_all(workdir) or {}
16 }
17 lib_src := os.join_path(workdir, 'lib.v')
18 lib_c := os.join_path(workdir, 'lib.c')
19 os.write_file(lib_src, [
20 'module main',
21 '',
22 "@[export: 'libfoo_ping']",
23 'fn ping() int {',
24 '\treturn 1',
25 '}',
26 ].join('\n')) or { panic(err) }
27 res :=
28 os.execute('${os.quoted_path(vexe)} -gc boehm -shared -o ${os.quoted_path(lib_c)} ${os.quoted_path(lib_src)}')
29 assert res.exit_code == 0, 'shared-lib codegen failed:\n${res.output}'
30 c_src := os.read_file(lib_c) or { panic(err) }
31 // The call must appear inside `_vinit`, not in a separate constructor:
32 // `_vinit_caller` already runs `GC_INIT()` via gen_shared_library_boehm_init,
33 // and calls `_vinit(0,0)` right after, so `GC_allow_register_threads()`
34 // inside `_vinit` is sequenced correctly.
35 vinit_start := c_src.index('void _vinit(int ___argc, voidptr ___argv) {') or {
36 assert false, '`_vinit` definition not found in generated C'
37 return
38 }
39 // `_vinit`'s body has no nested top-level `\n}\n`; the next one closes it.
40 vinit_end := c_src.index_after('\n}\n', vinit_start) or {
41 assert false, '`_vinit` body terminator not found in generated C'
42 return
43 }
44 vinit_body := c_src[vinit_start..vinit_end]
45 assert vinit_body.contains('GC_allow_register_threads();'), 'expected `GC_allow_register_threads()` inside `_vinit`, got:\n${vinit_body}'
46 assert vinit_body.contains('#if defined(_VGCBOEHM) && defined(GC_THREADS)'), 'expected `GC_THREADS` guard around the call, got:\n${vinit_body}'
47}
48