v2 / vlib / v / gen / c / spawn_and_go.v
580 lines · 571 sloc · 20.47 KB · 6b20a5ee7d2b42d98e601da4bef94a01d259e311
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module c
5
6import v.ast
7import v.util
8
9enum SpawnGoMode {
10 spawn_
11 go_
12}
13
14fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) {
15 if node.call_expr.should_be_skipped {
16 return
17 }
18 is_spawn := mode == .spawn_
19 is_go := mode == .go_
20 if is_spawn {
21 g.writeln('/*spawn (thread) */')
22 } else {
23 g.writeln('/*go (coroutine) */')
24 }
25 line := g.go_before_last_stmt()
26 mut handle := ''
27 tmp := g.new_tmp_var()
28 mut expr := node.call_expr
29 mut name := expr.name
30 mut use_tmp_fn_var := false
31 tmp_fn := g.new_tmp_var()
32
33 if expr.is_fn_var {
34 // generate a name different for same var fn name declared in another scope
35 name = '${name}_${node.pos.pos}'
36 }
37
38 if expr.concrete_types.len > 0 {
39 name = g.generic_fn_name(expr.concrete_types, name)
40 } else if expr.is_fn_var && expr.fn_var_type.has_flag(.generic) {
41 fn_var_type := g.unwrap_generic(expr.fn_var_type)
42 name = g.styp(fn_var_type)
43 }
44
45 if expr.is_method {
46 receiver_sym := g.table.sym(g.unwrap_generic(expr.receiver_type))
47 name = receiver_sym.cname + '_' + name
48 if receiver_sym.is_builtin() && !name.starts_with('_') {
49 name = 'builtin__${name}'
50 }
51 } else if mut expr.left is ast.AnonFn {
52 if expr.left.inherited_vars.len > 0 {
53 fn_var := g.fn_var_signature(ast.void_type, expr.left.decl.return_type,
54 expr.left.decl.params.map(it.typ), tmp_fn)
55 g.write('\t${fn_var} = ')
56 g.gen_anon_fn(mut expr.left)
57 g.writeln(';')
58 use_tmp_fn_var = true
59 name = g.anon_fn_cname(expr.left.decl.return_type, expr.left.decl.params.map(it.typ))
60 } else {
61 g.gen_anon_fn_decl(mut expr.left)
62 name = expr.left.decl.name
63 }
64 } else if expr.left is ast.IndexExpr {
65 if expr.is_fn_var {
66 fn_sym := g.table.sym(expr.fn_var_type)
67 func := (fn_sym.info as ast.FnType).func
68 fn_var := g.fn_var_signature(ast.void_type, func.return_type, func.params.map(it.typ),
69 tmp_fn)
70 g.write('\t${fn_var} = ')
71 g.expr(expr.left)
72 g.writeln(';')
73 name = fn_sym.cname
74 use_tmp_fn_var = true
75 }
76 }
77 // When inside a generic function, differentiate wrapper struct names
78 // per concrete type instantiation so each gets its own typed struct/wrapper.
79 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
80 generic_suffix := g.generic_fn_name(g.cur_concrete_types, '')
81 if generic_suffix != '' && !name.ends_with(generic_suffix) {
82 name = g.generic_fn_name(g.cur_concrete_types, name)
83 }
84 }
85 name = util.no_dots(name)
86 g.empty_line = true
87 g.writeln('// start go')
88 wrapper_struct_name := 'thread_arg_' + name
89 wrapper_fn_name := name + '_thread_wrapper'
90 arg_tmp_var := 'arg_' + tmp
91 if is_spawn {
92 if g.pref.prealloc {
93 g.writeln('${wrapper_struct_name} *${arg_tmp_var} = (${wrapper_struct_name} *) malloc(sizeof(thread_arg_${name}));')
94 g.writeln('if (${arg_tmp_var} == NULL) builtin___v_panic(_S("thread argument allocation failed"));')
95 } else {
96 g.writeln('${wrapper_struct_name} *${arg_tmp_var} = (${wrapper_struct_name} *) builtin___v_malloc(sizeof(thread_arg_${name}));')
97 }
98 } else if is_go {
99 g.writeln('${wrapper_struct_name} ${arg_tmp_var};')
100 }
101 dot := if is_spawn { '->' } else { '.' }
102 fn_name := if use_tmp_fn_var {
103 tmp_fn
104 } else if expr.is_fn_var {
105 expr.name
106 } else {
107 if func := g.table.find_fn(expr.name) {
108 if func.mod == 'builtin' && !name.starts_with('builtin__') && func.language != .c {
109 'builtin__${name}'
110 } else {
111 name
112 }
113 } else {
114 name
115 }
116 }
117 if !(expr.is_method && (g.table.sym(expr.receiver_type).kind == .interface
118 || (g.table.sym(expr.receiver_type).kind == .struct && expr.is_field))) {
119 g.writeln('${arg_tmp_var}${dot}fn = ${fn_name};')
120 }
121 if expr.is_method {
122 g.write('${arg_tmp_var}${dot}arg0 = ')
123 if mut expr.left is ast.Ident && expr.left.obj.typ.is_ptr()
124 && !node.call_expr.receiver_type.is_ptr() {
125 g.write('*'.repeat(expr.left.obj.typ.nr_muls()))
126 }
127 g.expr(expr.left)
128 g.writeln(';')
129 }
130 for i, arg in expr.args {
131 g.write('${arg_tmp_var}${dot}arg${i + 1} = ')
132 g.expr(arg.expr)
133 g.writeln(';')
134 }
135 if is_spawn && g.pref.prealloc {
136 g.writeln('${arg_tmp_var}->prealloc_scope = builtin__prealloc_scope_retain_current();')
137 }
138 call_ret_type := if expr.is_fn_var && g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
139 && g.cur_fn.generic_names.len > 0 {
140 // In generic contexts, node.call_expr.return_type may be stale from
141 // a previous instantiation. Look up the original fn param from the table
142 // and resolve through convert_generic_type.
143 mut resolved_ret := g.unwrap_generic(node.call_expr.return_type)
144 cur_fn_name := g.cur_fn.fkey()
145 orig_fn := g.table.find_fn(cur_fn_name) or { ast.Fn{} }
146 for param in orig_fn.params {
147 if param.name == expr.name {
148 if param.typ.has_flag(.generic) || g.type_has_unresolved_generic_parts(param.typ) {
149 mut muttable := unsafe { &ast.Table(g.table) }
150 if resolved_type := muttable.convert_generic_type(param.typ,
151 orig_fn.generic_names, g.cur_concrete_types)
152 {
153 fn_sym := g.table.sym(resolved_type)
154 if fn_sym.info is ast.FnType {
155 resolved_ret = fn_sym.info.func.return_type
156 }
157 }
158 }
159 break
160 }
161 }
162 resolved_ret
163 } else {
164 g.unwrap_generic(node.call_expr.return_type)
165 }
166 s_ret_typ := g.styp(g.unwrap_generic(call_ret_type))
167 if g.pref.os == .windows && call_ret_type != ast.void_type {
168 if g.pref.prealloc {
169 g.writeln('${arg_tmp_var}->ret_ptr = (void *) malloc(sizeof(${s_ret_typ}));')
170 g.writeln('if (${arg_tmp_var}->ret_ptr == NULL) builtin___v_panic(_S("thread return allocation failed"));')
171 } else {
172 g.writeln('${arg_tmp_var}->ret_ptr = (void *) builtin___v_malloc(sizeof(${s_ret_typ}));')
173 }
174 }
175 gohandle_name := g.gen_gohandle_name(call_ret_type)
176 if is_spawn {
177 if g.pref.os == .windows {
178 simple_handle := if node.is_expr && call_ret_type != ast.void_type {
179 'thread_handle_${tmp}'
180 } else {
181 'thread_${tmp}'
182 }
183 stack_size := g.get_cur_thread_stack_size(expr.name)
184 g.writeln('HANDLE ${simple_handle} = CreateThread(0, ${stack_size}, (LPTHREAD_START_ROUTINE)${wrapper_fn_name}, ${arg_tmp_var}, 0, 0); // fn: ${expr.name}')
185 g.writeln('if (!${simple_handle}) builtin__panic_lasterr(builtin__tos3("`go ${name}()`: "));')
186 if node.is_expr && call_ret_type != ast.void_type {
187 g.writeln('${gohandle_name} thread_${tmp} = {')
188 g.writeln('\t.ret_ptr = ${arg_tmp_var}->ret_ptr,')
189 g.writeln2('\t.handle = thread_handle_${tmp}', '};')
190 }
191 if !node.is_expr {
192 g.writeln('CloseHandle(thread_${tmp});')
193 }
194 } else {
195 g.writeln('pthread_t thread_${tmp};')
196 mut sthread_attributes := 'NULL'
197 if g.pref.os != .vinix {
198 g.writeln('pthread_attr_t thread_${tmp}_attributes;')
199 g.writeln('pthread_attr_init(&thread_${tmp}_attributes);')
200 size := g.get_cur_thread_stack_size(expr.name)
201 g.writeln('pthread_attr_setstacksize(&thread_${tmp}_attributes, ${size}); // fn: ${expr.name}')
202 sthread_attributes = '&thread_${tmp}_attributes'
203 }
204 g.writeln('int ${tmp}_thr_res = pthread_create(&thread_${tmp}, ${sthread_attributes}, (void*)${wrapper_fn_name}, ${arg_tmp_var});')
205 g.writeln('if (${tmp}_thr_res) builtin__panic_error_number(builtin__tos3("`go ${name}()`: "), ${tmp}_thr_res);')
206 if !node.is_expr {
207 g.writeln('pthread_detach(thread_${tmp});')
208 }
209 }
210 } else if is_go {
211 if util.nr_jobs > 1 {
212 g.writeln('photon_thread_create_and_migrate_to_work_pool((void*)${wrapper_fn_name}, &${arg_tmp_var});')
213 } else {
214 g.writeln('photon_thread_create((void*)${wrapper_fn_name}, &${arg_tmp_var}, 8 * 1024);')
215 }
216 }
217 g.writeln('// end go')
218 if node.is_expr {
219 handle = 'thread_${tmp}'
220 g.create_waiter_handler(call_ret_type, s_ret_typ, gohandle_name)
221 }
222 // Register the wrapper type and function
223 mut should_register := false
224 lock g.threaded_fns {
225 if name !in g.threaded_fns {
226 g.threaded_fns << name
227 should_register = true
228 }
229 }
230 if should_register {
231 g.type_definitions.writeln('\ntypedef struct ${wrapper_struct_name} {')
232 mut fn_var := ''
233 mut wrapper_return_type := call_ret_type
234 mut resolved_fn_params := []ast.Param{}
235 if node.call_expr.is_fn_var {
236 mut fn_var_type := node.call_expr.fn_var_type
237 // In generic contexts, fn_var_type may be stale from the last checker pass.
238 // Look up the original fn parameter from the table to get the generic fn type,
239 // then resolve it through convert_generic_type.
240 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0
241 && g.cur_fn.generic_names.len > 0 {
242 cur_fn_name := g.cur_fn.fkey()
243 orig_fn := g.table.find_fn(cur_fn_name) or { ast.Fn{} }
244 for param in orig_fn.params {
245 if param.name == expr.name {
246 if param.typ.has_flag(.generic)
247 || g.type_has_unresolved_generic_parts(param.typ) {
248 mut muttable := unsafe { &ast.Table(g.table) }
249 if resolved := muttable.convert_generic_type(param.typ,
250 orig_fn.generic_names, g.cur_concrete_types)
251 {
252 fn_var_type = resolved
253 }
254 }
255 break
256 }
257 }
258 }
259 fn_sym := g.table.sym(fn_var_type)
260 info := fn_sym.info as ast.FnType
261 resolved_fn_params = info.func.params.clone()
262 wrapper_return_type = info.func.return_type
263 fn_var = g.fn_var_signature(ast.void_type, wrapper_return_type,
264 info.func.params.map(it.typ), 'fn')
265 } else if node.call_expr.left is ast.AnonFn {
266 f := node.call_expr.left.decl
267 wrapper_return_type = f.return_type
268 fn_var =
269 g.fn_var_signature(ast.void_type, wrapper_return_type, f.params.map(it.typ), 'fn')
270 } else {
271 if node.call_expr.is_method {
272 rec_sym := g.table.sym(g.unwrap_generic(node.call_expr.receiver_type))
273 if f := rec_sym.find_method_with_generic_parent(node.call_expr.name) {
274 mut muttable := unsafe { &ast.Table(g.table) }
275 return_type := muttable.convert_generic_type(f.return_type, f.generic_names,
276 node.call_expr.concrete_types) or { f.return_type }
277 wrapper_return_type = return_type
278 mut arg_types := f.params.map(it.typ)
279 arg_types = arg_types.map(muttable.convert_generic_type(it, f.generic_names,
280 node.call_expr.concrete_types) or { it })
281 fn_var = g.fn_var_signature(ast.void_type, return_type, arg_types, 'fn')
282 }
283 } else {
284 if f := g.table.find_fn(node.call_expr.name) {
285 concrete_types := node.call_expr.concrete_types.map(g.unwrap_generic(it))
286 return_type := g.table.convert_generic_type(f.return_type, f.generic_names,
287 concrete_types) or { f.return_type }
288 wrapper_return_type = return_type
289 mut arg_types := f.params.map(it.typ)
290 arg_types = arg_types.map(g.table.convert_generic_type(it, f.generic_names,
291 concrete_types) or { it })
292 for i, typ in arg_types {
293 mut typ_sym := g.table.sym(typ)
294 for {
295 if mut typ_sym.info is ast.Array {
296 typ_sym = g.table.sym(typ_sym.info.elem_type)
297 } else {
298 if typ_sym.info is ast.FnType {
299 arg_types[i] = expr.args[i].typ
300 }
301 break
302 }
303 }
304 }
305 fn_var = g.fn_var_signature(ast.void_type, return_type, arg_types, 'fn')
306 }
307 }
308 }
309 wrapper_return_type = g.unwrap_generic(wrapper_return_type)
310 wrapper_s_ret_typ := g.styp(wrapper_return_type)
311 if fn_var != '' {
312 g.type_definitions.writeln('\t${fn_var};')
313 }
314 if expr.is_method {
315 styp := g.styp(expr.receiver_type)
316 g.type_definitions.writeln('\t${styp} arg0;')
317 }
318 need_return_ptr := g.pref.os == .windows && wrapper_return_type != ast.void_type
319 for i, arg in expr.args {
320 mut arg_typ := arg.typ
321 // For fn var and AnonFn calls in generic contexts, use the declared
322 // parameter types instead of argument expression types, since the
323 // AST arg types may be stale from a previous generic instantiation.
324 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
325 if expr.left is ast.AnonFn {
326 anon := expr.left as ast.AnonFn
327 f := anon.decl
328 if i < f.params.len {
329 arg_typ = g.unwrap_generic(f.params[i].typ)
330 }
331 } else if expr.is_fn_var && resolved_fn_params.len > 0 {
332 if i < resolved_fn_params.len {
333 arg_typ = resolved_fn_params[i].typ
334 }
335 } else {
336 resolved_arg_typ := g.unwrap_generic(arg_typ)
337 if resolved_arg_typ != 0 {
338 arg_typ = resolved_arg_typ
339 }
340 }
341 }
342 arg_sym := g.table.sym(arg_typ)
343 if arg_sym.info is ast.FnType {
344 sig := g.fn_var_signature(arg_typ, arg_sym.info.func.return_type,
345 arg_sym.info.func.params.map(it.typ), 'arg${i + 1}')
346 g.type_definitions.writeln('\t' + sig + ';')
347 } else {
348 styp := g.styp(arg_typ)
349 g.type_definitions.writeln('\t${styp} arg${i + 1};')
350 }
351 }
352 if is_spawn && g.pref.prealloc {
353 g.type_definitions.writeln('\tvoid* prealloc_scope;')
354 }
355 if need_return_ptr {
356 g.type_definitions.writeln('\tvoid* ret_ptr;')
357 }
358 g.type_definitions.writeln('} ${wrapper_struct_name};')
359 thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' }
360 g.waiter_fn_definitions.writeln('${g.static_non_parallel}${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg);')
361 g.gowrappers.writeln('${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg) {')
362 if is_spawn && g.pref.prealloc && wrapper_return_type == ast.void_type {
363 g.gowrappers.writeln('\tvoid* thread_prealloc_scope = builtin__prealloc_scope_begin();')
364 }
365 if wrapper_return_type != ast.void_type {
366 if g.pref.os == .windows {
367 g.gowrappers.write_string('\t*((${wrapper_s_ret_typ}*)(arg->ret_ptr)) = ')
368 } else {
369 if g.pref.prealloc {
370 g.gowrappers.writeln('\t${wrapper_s_ret_typ}* ret_ptr = (${wrapper_s_ret_typ}*) malloc(sizeof(${wrapper_s_ret_typ}));')
371 g.gowrappers.writeln('\tif (ret_ptr == NULL) builtin___v_panic(_S("thread return allocation failed"));')
372 } else {
373 g.gowrappers.writeln('\t${wrapper_s_ret_typ}* ret_ptr = (${wrapper_s_ret_typ}*) builtin___v_malloc(sizeof(${wrapper_s_ret_typ}));')
374 }
375 $if tinyc && arm64 {
376 g.gowrappers.write_string('\t${wrapper_s_ret_typ} tcc_bug_tmp_var = ')
377 } $else {
378 g.gowrappers.write_string('\t*ret_ptr = ')
379 }
380 }
381 } else {
382 g.gowrappers.write_string('\t')
383 }
384 if expr.is_method {
385 unwrapped_rec_type := g.unwrap_generic(expr.receiver_type)
386 typ_sym := g.table.sym(unwrapped_rec_type)
387 if typ_sym.kind == .interface
388 && (typ_sym.info as ast.Interface).defines_method(expr.name) {
389 rec_cc_type := g.cc_type(unwrapped_rec_type, false)
390 receiver_type_name := util.no_dots(rec_cc_type)
391 g.gowrappers.write_string2('${c_name(receiver_type_name)}_name_table[', 'arg->arg0')
392 dot_or_ptr := g.dot_or_ptr(unwrapped_rec_type)
393 mname := c_name(expr.name)
394 g.gowrappers.write_string2('${dot_or_ptr}_typ]._method_${mname}(', 'arg->arg0')
395 g.gowrappers.write_string('${dot_or_ptr}_object')
396 } else if typ_sym.kind == .struct && expr.is_field {
397 g.gowrappers.write_string('arg->arg0')
398 idot := if expr.left_type.is_ptr() { '->' } else { '.' }
399 mname := c_name(expr.name)
400 g.gowrappers.write_string('${idot}${mname}(')
401 } else {
402 g.gowrappers.write_string2('arg->fn(', 'arg->arg0')
403 }
404 if expr.args.len > 0 && (typ_sym.kind != .struct || !expr.is_field) {
405 g.gowrappers.write_string(', ')
406 }
407 } else {
408 g.gowrappers.write_string('arg->fn(')
409 }
410 if expr.args.len > 0 {
411 mut has_cast := false
412 for i in 0 .. expr.args.len {
413 if g.table.sym(expr.expected_arg_types[i]).kind == .interface
414 && g.table.sym(expr.args[i].typ).kind != .interface {
415 has_cast = true
416 break
417 }
418 }
419 if has_cast {
420 pos := g.out.len
421 g.call_args(expr)
422 mut call_args_str := g.out.after(pos).trim_space()
423 g.go_back(call_args_str.len)
424 mut rep_group := []string{cap: 2 * expr.args.len}
425 for i in 0 .. expr.args.len {
426 rep_group << g.expr_string(expr.args[i].expr)
427 rep_group << 'arg->arg${i + 1}'
428 }
429 if call_args_str.starts_with('I_') {
430 g.gowrappers.write_string(call_args_str.all_before('('))
431 g.gowrappers.write_string('(')
432 g.gowrappers.write_string(call_args_str.all_after('(').replace_each(rep_group))
433 } else {
434 call_args_str = call_args_str.replace_each(rep_group)
435 g.gowrappers.write_string(call_args_str)
436 }
437 } else if expr.name in ['print', 'println', 'eprint', 'eprintln', 'panic']
438 && expr.args[0].typ != ast.string_type {
439 pos := g.out.len
440 g.gen_expr_to_string(expr.args[0].expr, expr.args[0].typ)
441 mut call_args_str := g.out.after(pos)
442 g.out.go_back(call_args_str.len)
443 mut rep_group := []string{cap: 2 * expr.args.len}
444 for i in 0 .. expr.args.len {
445 rep_group << g.expr_string(expr.args[i].expr)
446 rep_group << 'arg->arg${i + 1}'
447 }
448 call_args_str = call_args_str.replace_each(rep_group)
449 g.gowrappers.write_string(call_args_str)
450 } else {
451 for i in 0 .. expr.args.len {
452 expected_nr_muls := expr.expected_arg_types[i].nr_muls()
453 arg_nr_muls := expr.args[i].typ.nr_muls()
454 if arg_nr_muls > expected_nr_muls {
455 g.gowrappers.write_string('*'.repeat(arg_nr_muls - expected_nr_muls))
456 } else if arg_nr_muls < expected_nr_muls {
457 g.gowrappers.write_string('&'.repeat(expected_nr_muls - arg_nr_muls))
458 }
459 g.gowrappers.write_string('arg->arg${i + 1}')
460 if i != expr.args.len - 1 {
461 g.gowrappers.write_string(', ')
462 }
463 }
464 }
465 }
466 g.gowrappers.writeln(');')
467 $if tinyc && arm64 {
468 if g.pref.os != .windows && wrapper_return_type != ast.void_type {
469 g.gowrappers.writeln('\t*ret_ptr = tcc_bug_tmp_var;')
470 }
471 }
472 if is_spawn && g.pref.prealloc {
473 if wrapper_return_type == ast.void_type {
474 g.gowrappers.writeln('\tbuiltin__prealloc_scope_end(thread_prealloc_scope);')
475 }
476 g.gowrappers.writeln('\tbuiltin__prealloc_scope_release(arg->prealloc_scope);')
477 }
478 if is_spawn {
479 if g.pref.prealloc {
480 g.gowrappers.writeln('\tfree(arg);')
481 } else {
482 g.gowrappers.writeln('\tbuiltin___v_free(arg);')
483 }
484 }
485 if g.pref.os != .windows && wrapper_return_type != ast.void_type {
486 g.gowrappers.writeln('\treturn ret_ptr;')
487 } else {
488 g.gowrappers.writeln('\treturn 0;')
489 }
490 g.gowrappers.writeln('}')
491 }
492 if node.is_expr {
493 g.empty_line = false
494 g.write2(line, handle)
495 }
496}
497
498// get current thread size, if fn hasn't defined return default
499@[inline]
500fn (mut g Gen) get_cur_thread_stack_size(name string) string {
501 ast_fn := g.table.fns[name] or { return '${g.pref.thread_stack_size}' }
502 attrs := ast_fn.attrs
503 if isnil(attrs) {
504 return '${g.pref.thread_stack_size}'
505 }
506 for attr in attrs {
507 if attr.name == 'spawn_stack' {
508 return attr.arg
509 }
510 }
511 return '${g.pref.thread_stack_size}'
512}
513
514fn (mut g Gen) gen_gohandle_name(typ ast.Type) string {
515 mut gohandle_name := ''
516 if typ == ast.void_type {
517 if typ.has_flag(.option) {
518 gohandle_name = '__v_thread_Option_void'
519 } else if typ.has_flag(.result) {
520 gohandle_name = '__v_thread_Result_void'
521 } else {
522 gohandle_name = '__v_thread'
523 }
524 } else {
525 ret_styp := g.styp(g.unwrap_generic(typ)).replace('*', '_ptr')
526 gohandle_name = '__v_thread_${ret_styp}'
527 }
528 return gohandle_name
529}
530
531fn (mut g Gen) create_waiter_handler(call_ret_type ast.Type, s_ret_typ string, gohandle_name string) {
532 // create wait handler for this return type if none exists
533 waiter_fn_name := gohandle_name + '_wait'
534 mut should_register := false
535 lock g.waiter_fns {
536 if waiter_fn_name !in g.waiter_fns {
537 g.waiter_fns << waiter_fn_name
538 should_register = true
539 }
540 }
541 if !should_register {
542 return
543 }
544 g.waiter_fn_definitions.writeln('${s_ret_typ} ${waiter_fn_name}(${gohandle_name} thread);')
545 g.gowrappers.writeln('\n${s_ret_typ} ${waiter_fn_name}(${gohandle_name} thread) {')
546 mut c_ret_ptr_ptr := 'NULL'
547 if call_ret_type != ast.void_type {
548 g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr;')
549 c_ret_ptr_ptr = '&ret_ptr'
550 }
551 if g.pref.os == .windows {
552 if call_ret_type == ast.void_type {
553 g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);')
554 } else {
555 g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);')
556 g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;')
557 }
558 } else {
559 g.gowrappers.writeln('\tif ((unsigned long int)thread == 0) { builtin___v_panic(_S("unable to join thread")); }')
560 g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)${c_ret_ptr_ptr});')
561 }
562 g.gowrappers.writeln('\tif (stat != 0) { builtin___v_panic(_S("unable to join thread")); }')
563 if g.pref.os == .windows {
564 if call_ret_type == ast.void_type {
565 g.gowrappers.writeln('\tCloseHandle(thread);')
566 } else {
567 g.gowrappers.writeln('\tCloseHandle(thread.handle);')
568 }
569 }
570 if call_ret_type != ast.void_type {
571 g.gowrappers.writeln('\t${s_ret_typ} ret = *ret_ptr;')
572 if g.pref.prealloc {
573 g.gowrappers.writeln('\tfree(ret_ptr);')
574 } else {
575 g.gowrappers.writeln('\tbuiltin___v_free(ret_ptr);')
576 }
577 g.gowrappers.writeln('\treturn ret;')
578 }
579 g.gowrappers.writeln('}')
580}
581