diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 72fb39c69b439b..4110433d07a1b5 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1593,6 +1593,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) { } } return + } else if typ_sym.info is ast.Thread { + waiter_fn_name := g.gen_gohandle_name(typ_sym.info.return_type) + g.create_waiter_handler(node, g.styp(typ_sym.info.return_type), waiter_fn_name) } left_sym := g.table.sym(left_type) final_left_sym := g.table.final_sym(left_type) diff --git a/vlib/v/gen/c/spawn_and_go.v b/vlib/v/gen/c/spawn_and_go.v index f16285eaba1135..e30470348aa15d 100644 --- a/vlib/v/gen/c/spawn_and_go.v +++ b/vlib/v/gen/c/spawn_and_go.v @@ -122,22 +122,7 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { if g.pref.os == .windows && node.call_expr.return_type != ast.void_type { g.writeln('${arg_tmp_var}->ret_ptr = (void *) _v_malloc(sizeof(${s_ret_typ}));') } - is_opt := node.call_expr.return_type.has_flag(.option) - is_res := node.call_expr.return_type.has_flag(.result) - mut gohandle_name := '' - if node.call_expr.return_type == ast.void_type { - if is_opt { - gohandle_name = '__v_thread_Option_void' - } else if is_res { - gohandle_name = '__v_thread_Result_void' - } else { - gohandle_name = '__v_thread' - } - } else { - ret_styp := g.styp(g.unwrap_generic(node.call_expr.return_type)).replace('*', - '_ptr') - gohandle_name = '__v_thread_${ret_styp}' - } + gohandle_name := g.gen_gohandle_name(node.call_expr.return_type) if is_spawn { if g.pref.os == .windows { simple_handle := if node.is_expr && node.call_expr.return_type != ast.void_type { @@ -182,50 +167,100 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { g.writeln('// end go') if node.is_expr { handle = 'thread_${tmp}' - // create wait handler for this return type if none exists - waiter_fn_name := gohandle_name + '_wait' - mut should_register := false - lock g.waiter_fns { - if waiter_fn_name !in g.waiter_fns { - g.waiter_fns << waiter_fn_name - should_register = true - } + g.create_waiter_handler(node.call_expr, s_ret_typ, gohandle_name) + } + // Register the wrapper type and function + g.create_thread_type(node.call_expr, s_ret_typ, name, is_spawn) + + if node.is_expr { + g.empty_line = false + g.write2(line, handle) + } +} + +// fn (mut g Gen) go_expr(node ast.GoExpr) { +//} + +// get current thread size, if fn hasn't defined return default +@[inline] +fn (mut g Gen) get_cur_thread_stack_size(name string) string { + ast_fn := g.table.fns[name] or { return '${g.pref.thread_stack_size}' } + attrs := ast_fn.attrs + if isnil(attrs) { + return '${g.pref.thread_stack_size}' + } + for attr in attrs { + if attr.name == 'spawn_stack' { + return attr.arg } - if should_register { - g.waiter_fn_definitions.writeln('${s_ret_typ} ${waiter_fn_name}(${gohandle_name} thread);') - g.gowrappers.writeln('\n${s_ret_typ} ${waiter_fn_name}(${gohandle_name} thread) {') - mut c_ret_ptr_ptr := 'NULL' - if node.call_expr.return_type != ast.void_type { - g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr;') - c_ret_ptr_ptr = '&ret_ptr' - } - if g.pref.os == .windows { - if node.call_expr.return_type == ast.void_type { - g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);') - } else { - g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);') - g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;') - } - } else { - g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)${c_ret_ptr_ptr});') - } - g.gowrappers.writeln('\tif (stat != 0) { _v_panic(_SLIT("unable to join thread")); }') - if g.pref.os == .windows { - if node.call_expr.return_type == ast.void_type { - g.gowrappers.writeln('\tCloseHandle(thread);') - } else { - g.gowrappers.writeln('\tCloseHandle(thread.handle);') - } - } - if node.call_expr.return_type != ast.void_type { - g.gowrappers.writeln('\t${s_ret_typ} ret = *ret_ptr;') - g.gowrappers.writeln('\t_v_free(ret_ptr);') - g.gowrappers.writeln('\treturn ret;') - } - g.gowrappers.writeln('}') + } + return '${g.pref.thread_stack_size}' +} + +fn (mut g Gen) gen_gohandle_name(typ ast.Type) string { + mut gohandle_name := '' + if typ == ast.void_type { + if typ.has_flag(.option) { + gohandle_name = '__v_thread_Option_void' + } else if typ.has_flag(.result) { + gohandle_name = '__v_thread_Result_void' + } else { + gohandle_name = '__v_thread' } + } else { + ret_styp := g.styp(g.unwrap_generic(typ)).replace('*', '_ptr') + gohandle_name = '__v_thread_${ret_styp}' } - // Register the wrapper type and function + return gohandle_name +} + +fn (mut g Gen) create_waiter_handler(call_expr ast.CallExpr, s_ret_typ string, gohandle_name string) { + // create wait handler for this return type if none exists + waiter_fn_name := gohandle_name + '_wait' + mut should_register := false + lock g.waiter_fns { + if waiter_fn_name !in g.waiter_fns { + g.waiter_fns << waiter_fn_name + should_register = true + } + } + if !should_register { + return + } + g.waiter_fn_definitions.writeln('${s_ret_typ} ${waiter_fn_name}(${gohandle_name} thread);') + g.gowrappers.writeln('\n${s_ret_typ} ${waiter_fn_name}(${gohandle_name} thread) {') + mut c_ret_ptr_ptr := 'NULL' + if call_expr.return_type != ast.void_type { + g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr;') + c_ret_ptr_ptr = '&ret_ptr' + } + if g.pref.os == .windows { + if call_expr.return_type == ast.void_type { + g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);') + } else { + g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);') + g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;') + } + } else { + g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)${c_ret_ptr_ptr});') + } + g.gowrappers.writeln('\tif (stat != 0) { _v_panic(_SLIT("unable to join thread")); }') + if g.pref.os == .windows { + if call_expr.return_type == ast.void_type { + g.gowrappers.writeln('\tCloseHandle(thread);') + } else { + g.gowrappers.writeln('\tCloseHandle(thread.handle);') + } + } + if call_expr.return_type != ast.void_type { + g.gowrappers.writeln('\t${s_ret_typ} ret = *ret_ptr;') + g.gowrappers.writeln('\t_v_free(ret_ptr);') + g.gowrappers.writeln('\treturn ret;') + } + g.gowrappers.writeln('}') +} + +fn (mut g Gen) create_thread_type(call_expr ast.CallExpr, s_ret_typ string, name string, is_spawn bool) { mut should_register := false lock g.threaded_fns { if name !in g.threaded_fns { @@ -233,199 +268,179 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { should_register = true } } - if should_register { - g.type_definitions.writeln('\ntypedef struct ${wrapper_struct_name} {') - mut fn_var := '' - if node.call_expr.is_fn_var { - fn_sym := g.table.sym(node.call_expr.fn_var_type) - info := fn_sym.info as ast.FnType - fn_var = g.fn_var_signature(info.func.return_type, info.func.params.map(it.typ), - 'fn') - } else if node.call_expr.left is ast.AnonFn { - f := node.call_expr.left.decl - fn_var = g.fn_var_signature(f.return_type, f.params.map(it.typ), 'fn') + if !should_register { + return + } + wrapper_struct_name := 'thread_arg_' + name + wrapper_fn_name := name + '_thread_wrapper' + + g.type_definitions.writeln('\ntypedef struct ${wrapper_struct_name} {') + mut fn_var := '' + if call_expr.is_fn_var { + fn_sym := g.table.sym(call_expr.fn_var_type) + info := fn_sym.info as ast.FnType + fn_var = g.fn_var_signature(info.func.return_type, info.func.params.map(it.typ), + 'fn') + } else if call_expr.left is ast.AnonFn { + f := call_expr.left.decl + fn_var = g.fn_var_signature(f.return_type, f.params.map(it.typ), 'fn') + } else { + if call_expr.is_method { + rec_sym := g.table.sym(g.unwrap_generic(call_expr.receiver_type)) + if f := rec_sym.find_method_with_generic_parent(call_expr.name) { + mut muttable := unsafe { &ast.Table(g.table) } + return_type := muttable.convert_generic_type(f.return_type, f.generic_names, + call_expr.concrete_types) or { f.return_type } + mut arg_types := f.params.map(it.typ) + arg_types = arg_types.map(muttable.convert_generic_type(it, f.generic_names, + call_expr.concrete_types) or { it }) + fn_var = g.fn_var_signature(return_type, arg_types, 'fn') + } } else { - if node.call_expr.is_method { - rec_sym := g.table.sym(g.unwrap_generic(node.call_expr.receiver_type)) - if f := rec_sym.find_method_with_generic_parent(node.call_expr.name) { - mut muttable := unsafe { &ast.Table(g.table) } - return_type := muttable.convert_generic_type(f.return_type, f.generic_names, - node.call_expr.concrete_types) or { f.return_type } - mut arg_types := f.params.map(it.typ) - arg_types = arg_types.map(muttable.convert_generic_type(it, f.generic_names, - node.call_expr.concrete_types) or { it }) - fn_var = g.fn_var_signature(return_type, arg_types, 'fn') - } - } else { - if f := g.table.find_fn(node.call_expr.name) { - concrete_types := node.call_expr.concrete_types.map(g.unwrap_generic(it)) - return_type := g.table.convert_generic_type(f.return_type, f.generic_names, - concrete_types) or { f.return_type } - mut arg_types := f.params.map(it.typ) - arg_types = arg_types.map(g.table.convert_generic_type(it, f.generic_names, - concrete_types) or { it }) - for i, typ in arg_types { - mut typ_sym := g.table.sym(typ) - for { - if mut typ_sym.info is ast.Array { - typ_sym = g.table.sym(typ_sym.info.elem_type) - } else { - if typ_sym.info is ast.FnType { - arg_types[i] = expr.args[i].typ - } - break + if f := g.table.find_fn(call_expr.name) { + concrete_types := call_expr.concrete_types.map(g.unwrap_generic(it)) + return_type := g.table.convert_generic_type(f.return_type, f.generic_names, + concrete_types) or { f.return_type } + mut arg_types := f.params.map(it.typ) + arg_types = arg_types.map(g.table.convert_generic_type(it, f.generic_names, + concrete_types) or { it }) + for i, typ in arg_types { + mut typ_sym := g.table.sym(typ) + for { + if mut typ_sym.info is ast.Array { + typ_sym = g.table.sym(typ_sym.info.elem_type) + } else { + if typ_sym.info is ast.FnType { + arg_types[i] = call_expr.args[i].typ } + break } } - fn_var = g.fn_var_signature(return_type, arg_types, 'fn') } + fn_var = g.fn_var_signature(return_type, arg_types, 'fn') } } - if fn_var != '' { - g.type_definitions.writeln('\t${fn_var};') + } + if fn_var != '' { + g.type_definitions.writeln('\t${fn_var};') + } + if call_expr.is_method { + styp := g.styp(call_expr.receiver_type) + g.type_definitions.writeln('\t${styp} arg0;') + } + need_return_ptr := g.pref.os == .windows && call_expr.return_type != ast.void_type + for i, arg in call_expr.args { + arg_sym := g.table.sym(arg.typ) + if arg_sym.info is ast.FnType { + sig := g.fn_var_signature(arg_sym.info.func.return_type, arg_sym.info.func.params.map(it.typ), + 'arg${i + 1}') + g.type_definitions.writeln('\t' + sig + ';') + } else { + styp := g.styp(arg.typ) + g.type_definitions.writeln('\t${styp} arg${i + 1};') } - if expr.is_method { - styp := g.styp(expr.receiver_type) - g.type_definitions.writeln('\t${styp} arg0;') + } + if need_return_ptr { + g.type_definitions.writeln('\tvoid* ret_ptr;') + } + g.type_definitions.writeln('} ${wrapper_struct_name};') + thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' } + g.type_definitions.writeln('${g.static_modifier} ${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg);') + g.gowrappers.writeln('${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg) {') + if call_expr.return_type != ast.void_type { + if g.pref.os == .windows { + g.gowrappers.write_string('\t*((${s_ret_typ}*)(arg->ret_ptr)) = ') + } else { + g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr = (${s_ret_typ}*) _v_malloc(sizeof(${s_ret_typ}));') + g.gowrappers.write_string('\t*ret_ptr = ') } - need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != ast.void_type - for i, arg in expr.args { - arg_sym := g.table.sym(arg.typ) - if arg_sym.info is ast.FnType { - sig := g.fn_var_signature(arg_sym.info.func.return_type, arg_sym.info.func.params.map(it.typ), - 'arg${i + 1}') - g.type_definitions.writeln('\t' + sig + ';') - } else { - styp := g.styp(arg.typ) - g.type_definitions.writeln('\t${styp} arg${i + 1};') - } + } else { + g.gowrappers.write_string('\t') + } + if call_expr.is_method { + unwrapped_rec_type := g.unwrap_generic(call_expr.receiver_type) + typ_sym := g.table.sym(unwrapped_rec_type) + if typ_sym.kind == .interface + && (typ_sym.info as ast.Interface).defines_method(call_expr.name) { + rec_cc_type := g.cc_type(unwrapped_rec_type, false) + receiver_type_name := util.no_dots(rec_cc_type) + g.gowrappers.write_string2('${c_name(receiver_type_name)}_name_table[', 'arg->arg0') + idot := if call_expr.left_type.is_ptr() { '->' } else { '.' } + mname := c_name(call_expr.name) + g.gowrappers.write_string2('${idot}_typ]._method_${mname}(', 'arg->arg0') + g.gowrappers.write_string('${idot}_object') + } else if typ_sym.kind == .struct && call_expr.is_field { + g.gowrappers.write_string('arg->arg0') + idot := if call_expr.left_type.is_ptr() { '->' } else { '.' } + mname := c_name(call_expr.name) + g.gowrappers.write_string('${idot}${mname}(') + } else { + g.gowrappers.write_string2('arg->fn(', 'arg->arg0') } - if need_return_ptr { - g.type_definitions.writeln('\tvoid* ret_ptr;') + if call_expr.args.len > 0 && (typ_sym.kind != .struct || !call_expr.is_field) { + g.gowrappers.write_string(', ') } - g.type_definitions.writeln('} ${wrapper_struct_name};') - thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' } - g.type_definitions.writeln('${g.static_modifier} ${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg);') - g.gowrappers.writeln('${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg) {') - if node.call_expr.return_type != ast.void_type { - if g.pref.os == .windows { - g.gowrappers.write_string('\t*((${s_ret_typ}*)(arg->ret_ptr)) = ') - } else { - g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr = (${s_ret_typ}*) _v_malloc(sizeof(${s_ret_typ}));') - g.gowrappers.write_string('\t*ret_ptr = ') + } else { + g.gowrappers.write_string('arg->fn(') + } + if call_expr.args.len > 0 { + mut has_cast := false + for i in 0 .. call_expr.args.len { + if g.table.sym(call_expr.expected_arg_types[i]).kind == .interface + && g.table.sym(call_expr.args[i].typ).kind != .interface { + has_cast = true + break } - } else { - g.gowrappers.write_string('\t') } - if expr.is_method { - unwrapped_rec_type := g.unwrap_generic(expr.receiver_type) - typ_sym := g.table.sym(unwrapped_rec_type) - if typ_sym.kind == .interface - && (typ_sym.info as ast.Interface).defines_method(expr.name) { - rec_cc_type := g.cc_type(unwrapped_rec_type, false) - receiver_type_name := util.no_dots(rec_cc_type) - g.gowrappers.write_string2('${c_name(receiver_type_name)}_name_table[', - 'arg->arg0') - idot := if expr.left_type.is_ptr() { '->' } else { '.' } - mname := c_name(expr.name) - g.gowrappers.write_string2('${idot}_typ]._method_${mname}(', 'arg->arg0') - g.gowrappers.write_string('${idot}_object') - } else if typ_sym.kind == .struct && expr.is_field { - g.gowrappers.write_string('arg->arg0') - idot := if expr.left_type.is_ptr() { '->' } else { '.' } - mname := c_name(expr.name) - g.gowrappers.write_string('${idot}${mname}(') - } else { - g.gowrappers.write_string2('arg->fn(', 'arg->arg0') + if has_cast { + pos := g.out.len + g.call_args(call_expr) + mut call_args_str := g.out.after(pos) + g.go_back(call_args_str.len) + mut rep_group := []string{cap: 2 * call_expr.args.len} + for i in 0 .. call_expr.args.len { + rep_group << g.expr_string(call_expr.args[i].expr) + rep_group << 'arg->arg${i + 1}' } - if expr.args.len > 0 && (typ_sym.kind != .struct || !expr.is_field) { - g.gowrappers.write_string(', ') + call_args_str = call_args_str.replace_each(rep_group) + g.gowrappers.write_string(call_args_str) + } else if call_expr.name in ['print', 'println', 'eprint', 'eprintln', 'panic'] + && call_expr.args[0].typ != ast.string_type { + pos := g.out.len + g.gen_expr_to_string(call_expr.args[0].expr, call_expr.args[0].typ) + mut call_args_str := g.out.after(pos) + g.out.go_back(call_args_str.len) + mut rep_group := []string{cap: 2 * call_expr.args.len} + for i in 0 .. call_expr.args.len { + rep_group << g.expr_string(call_expr.args[i].expr) + rep_group << 'arg->arg${i + 1}' } + call_args_str = call_args_str.replace_each(rep_group) + g.gowrappers.write_string(call_args_str) } else { - g.gowrappers.write_string('arg->fn(') - } - if expr.args.len > 0 { - mut has_cast := false - for i in 0 .. expr.args.len { - if g.table.sym(expr.expected_arg_types[i]).kind == .interface - && g.table.sym(expr.args[i].typ).kind != .interface { - has_cast = true - break - } - } - if has_cast { - pos := g.out.len - g.call_args(expr) - mut call_args_str := g.out.after(pos) - g.go_back(call_args_str.len) - mut rep_group := []string{cap: 2 * expr.args.len} - for i in 0 .. expr.args.len { - rep_group << g.expr_string(expr.args[i].expr) - rep_group << 'arg->arg${i + 1}' + for i in 0 .. call_expr.args.len { + expected_nr_muls := call_expr.expected_arg_types[i].nr_muls() + arg_nr_muls := call_expr.args[i].typ.nr_muls() + if arg_nr_muls > expected_nr_muls { + g.gowrappers.write_string('*'.repeat(arg_nr_muls - expected_nr_muls)) + } else if arg_nr_muls < expected_nr_muls { + g.gowrappers.write_string('&'.repeat(expected_nr_muls - arg_nr_muls)) } - call_args_str = call_args_str.replace_each(rep_group) - g.gowrappers.write_string(call_args_str) - } else if expr.name in ['print', 'println', 'eprint', 'eprintln', 'panic'] - && expr.args[0].typ != ast.string_type { - pos := g.out.len - g.gen_expr_to_string(expr.args[0].expr, expr.args[0].typ) - mut call_args_str := g.out.after(pos) - g.out.go_back(call_args_str.len) - mut rep_group := []string{cap: 2 * expr.args.len} - for i in 0 .. expr.args.len { - rep_group << g.expr_string(expr.args[i].expr) - rep_group << 'arg->arg${i + 1}' - } - call_args_str = call_args_str.replace_each(rep_group) - g.gowrappers.write_string(call_args_str) - } else { - for i in 0 .. expr.args.len { - expected_nr_muls := expr.expected_arg_types[i].nr_muls() - arg_nr_muls := expr.args[i].typ.nr_muls() - if arg_nr_muls > expected_nr_muls { - g.gowrappers.write_string('*'.repeat(arg_nr_muls - expected_nr_muls)) - } else if arg_nr_muls < expected_nr_muls { - g.gowrappers.write_string('&'.repeat(expected_nr_muls - arg_nr_muls)) - } - g.gowrappers.write_string('arg->arg${i + 1}') - if i != expr.args.len - 1 { - g.gowrappers.write_string(', ') - } + g.gowrappers.write_string('arg->arg${i + 1}') + if i != call_expr.args.len - 1 { + g.gowrappers.write_string(', ') } } } - g.gowrappers.writeln(');') - if is_spawn { - g.gowrappers.writeln('\t_v_free(arg);') - } - if g.pref.os != .windows && node.call_expr.return_type != ast.void_type { - g.gowrappers.writeln('\treturn ret_ptr;') - } else { - g.gowrappers.writeln('\treturn 0;') - } - g.gowrappers.writeln('}') - } - if node.is_expr { - g.empty_line = false - g.write2(line, handle) } -} - -// fn (mut g Gen) go_expr(node ast.GoExpr) { -//} - -// get current thread size, if fn hasn't defined return default -@[inline] -fn (mut g Gen) get_cur_thread_stack_size(name string) string { - ast_fn := g.table.fns[name] or { return '${g.pref.thread_stack_size}' } - attrs := ast_fn.attrs - if isnil(attrs) { - return '${g.pref.thread_stack_size}' + g.gowrappers.writeln(');') + if is_spawn { + g.gowrappers.writeln('\t_v_free(arg);') } - for attr in attrs { - if attr.name == 'spawn_stack' { - return attr.arg - } + if g.pref.os != .windows && call_expr.return_type != ast.void_type { + g.gowrappers.writeln('\treturn ret_ptr;') + } else { + g.gowrappers.writeln('\treturn 0;') } - return '${g.pref.thread_stack_size}' + g.gowrappers.writeln('}') } diff --git a/vlib/v/slow_tests/inout/thread_uninitialized_err.out b/vlib/v/slow_tests/inout/thread_uninitialized_err.out new file mode 100644 index 00000000000000..fa8f62c5c009b4 --- /dev/null +++ b/vlib/v/slow_tests/inout/thread_uninitialized_err.out @@ -0,0 +1,2 @@ +================ V panic ================ +V panic: unable to join thread \ No newline at end of file diff --git a/vlib/v/slow_tests/inout/thread_uninitialized_err.vv b/vlib/v/slow_tests/inout/thread_uninitialized_err.vv new file mode 100644 index 00000000000000..131f2d61c1bc80 --- /dev/null +++ b/vlib/v/slow_tests/inout/thread_uninitialized_err.vv @@ -0,0 +1,10 @@ +struct Client { +mut: + network_thread thread +} + +fn main() { + mut client := &Client{} + println('================ V panic ================') + client.network_thread.wait() +}