fastc: declare thread handle types at .wait() sites too (#31124)
The thread handle typedef and its waiter were only registered by `spawn`. A `[]thread T` that is waited on in code whose only `spawn` is compiled out by `$if` referenced both without defining them. The driver does this with its prestarted FastC workers, which only macOS spawns, so FastC builds of cmd/v failed on Linux with `'__vf_thread_kF_5f_5fFastcPrestartedCUnits' undeclared`. `.wait()` lowering now registers the typedef and waiter when the file has not done so yet.
This commit is contained in:
1 parent
c984dc6e33
commit
1be458cf31
3 files changed
+73
-7
No files matched your search
@@ -477,7 +477,7 @@ fn (g &Parser) render_method_call_expression(tokens []FastcExpressionToken, rend
|
||||
}
|
||||
mut w := unsafe { &Parser(g) }
|
||||
fastc_register_composite_type(result_type, mut w.composite_types)
|
||||
wait_helper := g.fastc_unclaimed_generated_name(fastc_thread_wait_name(flat.source_type_name(element)))
|
||||
wait_helper := g.fastc_thread_wait_helper(flat.source_type_name(element), value_type)
|
||||
recv_src := if flat.source_type_name(receiver_type).ends_with('*') {
|
||||
'*(${receiver.source})'
|
||||
} else {
|
||||
@@ -517,7 +517,7 @@ fn (g &Parser) render_method_call_expression(tokens []FastcExpressionToken, rend
|
||||
wait_end := fastc_matching_rpar(tokens, i + 1) or { continue }
|
||||
receiver := g.render_method_receiver_expression(receiver_tokens) or { continue }
|
||||
value_type := (g.fastc_thread_value_type(receiver_type) or { u32(0) })
|
||||
wait_call := '${g.fastc_unclaimed_generated_name(fastc_thread_wait_name(flat.source_type_name(receiver_type)))}(${receiver.source})'
|
||||
wait_call := '${g.fastc_thread_wait_helper(flat.source_type_name(receiver_type).trim_right('*'), value_type)}(${receiver.source})'
|
||||
if receiver_start == 0 && wait_end == tokens.len - 1 {
|
||||
return FastcRenderedExpression{
|
||||
source: wait_call
|
||||
|
||||
@@ -97,6 +97,18 @@ fn fastc_thread_wait_name(thread_type string) string {
|
||||
return '__vf_thread_wait_${thread_type.all_after(fastc_thread_type_prefix)}'
|
||||
}
|
||||
|
||||
// fastc_thread_wait_helper returns the waiter of one thread handle type,
|
||||
// registering its typedef and waiter when no `spawn` has done so yet.
|
||||
fn (g &Parser) fastc_thread_wait_helper(thread_type string, value_type_source_id u32) string {
|
||||
wait_name := g.fastc_unclaimed_generated_name(fastc_thread_wait_name(thread_type))
|
||||
if thread_type in g.spawn_typedefs && wait_name in g.spawn_helpers {
|
||||
return wait_name
|
||||
}
|
||||
// Rendering keeps an immutable parser; the helper maps are per-file output.
|
||||
mut w := unsafe { &Parser(g) }
|
||||
return w.register_thread_type_helpers(thread_type, value_type_source_id)
|
||||
}
|
||||
|
||||
// read_spawn_expression parses `spawn callee(arguments)` eagerly at an
|
||||
// expression start. The callee must be a collected plain or imported
|
||||
// function; its signature supplies the packed-argument field types.
|
||||
@@ -390,11 +402,10 @@ fn (g &Parser) generated_name_is_claimed(candidate string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// register_spawn_helpers records the thread typedef, waiter, packed-argument
|
||||
// struct, run wrapper, and creator for one spawn target. The result is the
|
||||
// first packed field, so the per-type waiter reads it without knowing which
|
||||
// call site produced the thread.
|
||||
fn (mut g Parser) register_spawn_helpers(function_key string, thread_type string, value_type_source_id u32, parameter_types []u32, start_name string, target_c_name string) {
|
||||
// register_thread_type_helpers records the typedef and waiter of one thread
|
||||
// handle type, and returns the waiter name. `.wait()` calls register them too:
|
||||
// a handle can be waited on in code whose only `spawn` is compiled out by `$if`.
|
||||
fn (mut g Parser) register_thread_type_helpers(thread_type string, value_type_source_id u32) string {
|
||||
value_type := flat.source_type_name(value_type_source_id)
|
||||
g.type_memo.clear()
|
||||
g.thread_value_types.set_id(thread_type, value_type_source_id)
|
||||
@@ -430,6 +441,16 @@ fn (mut g Parser) register_spawn_helpers(function_key string, thread_type string
|
||||
}
|
||||
g.spawn_helpers[wait_name] = waiter
|
||||
}
|
||||
return wait_name
|
||||
}
|
||||
|
||||
// register_spawn_helpers records the thread typedef, waiter, packed-argument
|
||||
// struct, run wrapper, and creator for one spawn target. The result is the
|
||||
// first packed field, so the per-type waiter reads it without knowing which
|
||||
// call site produced the thread.
|
||||
fn (mut g Parser) register_spawn_helpers(function_key string, thread_type string, value_type_source_id u32, parameter_types []u32, start_name string, target_c_name string) {
|
||||
value_type := flat.source_type_name(value_type_source_id)
|
||||
g.register_thread_type_helpers(thread_type, value_type_source_id)
|
||||
if start_name in g.spawn_helpers {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,3 +55,48 @@ fn main() {
|
||||
assert run.exit_code == 0, run.output
|
||||
assert run.output.trim_space() == 'thread arrays work', run.output
|
||||
}
|
||||
|
||||
fn test_fastc_waits_on_thread_handles_whose_spawn_is_compiled_out() {
|
||||
$if !macos && !linux {
|
||||
return
|
||||
}
|
||||
vroot := os.real_path(os.join_path(@DIR, '../../../..'))
|
||||
root := os.join_path(os.vtmp_dir(), 'fastc_thread_compiled_out_spawn_${os.getpid()}')
|
||||
os.mkdir_all(root) or { panic(err) }
|
||||
defer { os.rmdir_all(root) or {} }
|
||||
// The driver waits on prestarted workers that only macOS spawns; the
|
||||
// thread typedefs and waiters must exist without any reachable `spawn`.
|
||||
os.write_file(os.join_path(root, 'main.v'), r'module main
|
||||
struct Item { value int }
|
||||
fn item(value int) Item { return Item{value: value} }
|
||||
fn integer(value int) int { return value }
|
||||
fn nothing() {}
|
||||
fn main() {
|
||||
mut items := []thread Item{}
|
||||
mut integers := []thread int{}
|
||||
mut completed := []thread{}
|
||||
$if fastc_thread_never ? {
|
||||
items << spawn item(1)
|
||||
integers << spawn integer(2)
|
||||
completed << spawn nothing()
|
||||
}
|
||||
if items.len > 0 {
|
||||
assert items[0].wait().value == 1
|
||||
}
|
||||
values := integers.wait()
|
||||
completed.wait()
|
||||
println("waited ${items.len} ${values.len} ${completed.len}")
|
||||
}
|
||||
') or { panic(err) }
|
||||
program := os.join_path(root, 'program')
|
||||
build := cmdexec.run_in_merged_with_env(@VEXE, ['-b', 'fastc', '-nocache', '-gc', 'none', '-o',
|
||||
program, root], vroot, {
|
||||
'VEXE': @VEXE
|
||||
'VFLAGS': ''
|
||||
'V_MACOS_V3_NO_FALLBACK': '1'
|
||||
})
|
||||
assert build.exit_code == 0, build.output
|
||||
run := cmdexec.run(program, [])
|
||||
assert run.exit_code == 0, run.output
|
||||
assert run.output.trim_space() == 'waited 0 0 0', run.output
|
||||
}
|
||||
Reference in new issue
Block a user