From 5306b2788c40ba2cda68167ff7822082a8d1a104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kr=C3=BCger?= <45282134+UweKrueger@users.noreply.github.com> Date: Mon, 22 Feb 2021 14:08:52 +0100 Subject: [PATCH] all: extend `wait()` method to array of `thread` handles (#8840) --- cmd/tools/vtest-self.v | 1 + vlib/v/checker/checker.v | 24 ++++++++++--- vlib/v/gen/c/array.v | 13 +++++++ vlib/v/gen/c/cgen.v | 58 +++++++++++++++++++++++++++---- vlib/v/gen/c/fn.v | 4 +++ vlib/v/gen/js/builtin_types.v | 4 +-- vlib/v/parser/assign.v | 11 +----- vlib/v/parser/parse_type.v | 18 +++++++++- vlib/v/parser/pratt.v | 8 +++++ vlib/v/table/table.v | 24 ++++++------- vlib/v/table/types.v | 22 ++++++------ vlib/v/tests/go_array_wait_test.v | 38 ++++++++++++++++++++ 12 files changed, 177 insertions(+), 48 deletions(-) create mode 100644 vlib/v/tests/go_array_wait_test.v diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index e808952bc5..305b709c77 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -96,6 +96,7 @@ const ( 'vlib/v/tests/generic_chan_test.v', 'vlib/v/tests/generics_method_test.v', 'vlib/v/tests/generics_test.v', + 'vlib/v/tests/go_array_wait_test.v', 'vlib/v/tests/go_call_generic_fn_test.v', 'vlib/v/tests/go_wait_2_test.v', 'vlib/v/tests/interface_edge_cases/assign_to_interface_field_test.v', diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 7d402e6c9c..e308a8683f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -29,7 +29,7 @@ const ( valid_comp_if_platforms = ['amd64', 'aarch64', 'x64', 'x32', 'little_endian', 'big_endian'] valid_comp_if_other = ['js', 'debug', 'test', 'glibc', 'prealloc', 'no_bounds_checking'] array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', - 'contains', 'index'] + 'contains', 'index', 'wait'] ) pub struct Checker { @@ -1306,10 +1306,11 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { is_filter_map := method_name in ['filter', 'map'] is_sort := method_name == 'sort' is_slice := method_name == 'slice' + is_wait := method_name == 'wait' if is_slice && !c.is_builtin_mod { c.error('.slice() is a private method, use `x[start..end]` instead', call_expr.pos) } - if is_filter_map || is_sort { + if is_filter_map || is_sort || is_wait { array_info := left_type_sym.info as table.Array if is_filter_map { // position of `it` doesn't matter @@ -1328,6 +1329,19 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { } } elem_typ = array_info.elem_type + if is_wait { + elem_sym := c.table.get_type_symbol(elem_typ) + if elem_sym.kind == .thread { + if call_expr.args.len != 0 { + c.error('wait() does not have any arguments', call_expr.args[0].pos) + } + thread_ret_type := elem_sym.thread_info().return_type + call_expr.return_type = c.table.find_or_register_array(thread_ret_type) + } else { + c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)', + call_expr.left.position()) + } + } } // map/filter are supposed to have 1 arg only mut arg_type := left_type @@ -1409,8 +1423,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { if !c.check_types(arg_type, info.elem_type) && !c.check_types(left_type, arg_type) { c.error('cannot $method_name `$arg_sym.name` to `$left_type_sym.name`', arg_expr.position()) } - } else if left_type_sym.kind == .gohandle && method_name == 'wait' { - info := left_type_sym.info as table.GoHandle + } else if left_type_sym.kind == .thread && method_name == 'wait' { + info := left_type_sym.info as table.Thread if call_expr.args.len > 0 { c.error('wait() does not have any arguments', call_expr.args[0].pos) } @@ -3611,7 +3625,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { } ast.GoExpr { ret_type := c.call_expr(mut node.go_stmt.call_expr) - return c.table.find_or_register_gohandle(ret_type) + return c.table.find_or_register_thread(ret_type) } ast.ChanInit { return c.chan_init(mut node) diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index e138b9c674..16de80bee3 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -556,3 +556,16 @@ fn (mut g Gen) gen_array_index(node ast.CallExpr) { g.expr(node.args[0].expr) g.write(')') } + +fn (mut g Gen) gen_array_wait(node ast.CallExpr) { + arr := g.table.get_type_symbol(node.receiver_type) + thread_type := arr.array_info().elem_type + thread_sym := g.table.get_type_symbol(thread_type) + thread_ret_type := thread_sym.thread_info().return_type + eltyp := g.table.get_type_symbol(thread_ret_type).cname + fn_name := g.register_thread_array_wait_call(eltyp) + g.write('${fn_name}(') + g.expr(node.left) + g.write(')') +} + diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index e5e7091aa7..e374303180 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -619,6 +619,36 @@ fn (mut g Gen) find_or_register_shared(t table.Type, base string) string { return sh_typ } +fn (mut g Gen) register_thread_array_wait_call(eltyp string) string { + thread_typ := '__v_thread_$eltyp' + ret_typ := if eltyp == '' { 'void' } else { 'Array_$eltyp' } + thread_arr_typ := 'Array_$thread_typ' + fn_name := '${thread_arr_typ}_wait' + if fn_name !in g.waiter_fns { + g.waiter_fns << fn_name + if eltyp == 'void' { + g.gowrappers.writeln(' +void ${fn_name}($thread_arr_typ a) { + for (int i = 0; i < a.len; ++i) { + $thread_typ t = (($thread_typ*)a.data)[i]; + __v_thread_${eltyp}_wait(t); + } +}') + } else { + g.gowrappers.writeln(' +$ret_typ ${fn_name}($thread_arr_typ a) { + $ret_typ res = __new_array_with_default(a.len, a.len, sizeof($eltyp), 0); + for (int i = 0; i < a.len; ++i) { + $thread_typ t = (($thread_typ*)a.data)[i]; + (($eltyp*)res.data)[i] = __v_thread_${eltyp}_wait(t); + } + return res; +}') + } + } + return fn_name +} + fn (mut g Gen) register_chan_pop_optional_call(opt_el_type string, styp string) { if opt_el_type !in g.chan_pop_optionals { g.chan_pop_optionals << opt_el_type @@ -3097,20 +3127,34 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { g.write(')') } } else if op_is_eq_or_ne && left_sym.kind == .array && right_sym.kind == .array { - ptr_typ := g.gen_array_equality_fn(left_type) + ptr_typ := g.gen_array_equality_fn(left_type.clear_flag(.shared_f)) if node.op == .ne { g.write('!') } g.write('${ptr_typ}_arr_eq(') - if node.left_type.is_ptr() { + if node.left_type.is_ptr() && !node.left_type.has_flag(.shared_f) { g.write('*') } g.expr(node.left) + if node.left_type.has_flag(.shared_f) { + if node.left_type.is_ptr() { + g.write('->val') + } else { + g.write('.val') + } + } g.write(', ') - if node.right_type.is_ptr() { + if node.right_type.is_ptr() && !node.right_type.has_flag(.shared_f) { g.write('*') } g.expr(node.right) + if node.right_type.has_flag(.shared_f) { + if node.right_type.is_ptr() { + g.write('->val') + } else { + g.write('.val') + } + } g.write(')') } else if op_is_eq_or_ne && left_sym.kind == .array_fixed && right_sym.kind == .array_fixed { ptr_typ := g.gen_fixed_array_equality_fn(left_type) @@ -5330,15 +5374,15 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) { table.Alias { // table.Alias { TODO } - table.GoHandle { + table.Thread { if g.pref.os == .windows { - if name == 'gohandle_void' { + if name == '__v_thread_void' { g.type_definitions.writeln('typedef HANDLE $name;') } else { // Windows can only return `u32` (no void*) from a thread, so the // V gohandle must maintain a pointer to the return value g.type_definitions.writeln('typedef struct {') - g.type_definitions.writeln('\tvoid* ret_ptr;') + g.type_definitions.writeln('\tvoid* ret_ptr;') g.type_definitions.writeln('\tHANDLE handle;') g.type_definitions.writeln('} $name;') } @@ -5825,7 +5869,7 @@ fn (mut g Gen) go_stmt(node ast.GoStmt, joinable bool) string { if g.pref.os == .windows && node.call_expr.return_type != table.void_type { g.writeln('$arg_tmp_var->ret_ptr = malloc(sizeof($s_ret_typ));') } - gohandle_name := 'gohandle_' + + gohandle_name := '__v_thread_' + g.table.get_type_symbol(g.unwrap_generic(node.call_expr.return_type)).cname if g.pref.os == .windows { simple_handle := if joinable && node.call_expr.return_type != table.void_type { diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 1eccba696d..fc1bca944c 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -532,6 +532,10 @@ fn (mut g Gen) method_call(node ast.CallExpr) { g.gen_array_index(node) return } + 'wait' { + g.gen_array_wait(node) + return + } else {} } } diff --git a/vlib/v/gen/js/builtin_types.v b/vlib/v/gen/js/builtin_types.v index f55d222228..4a1bfe4a1d 100644 --- a/vlib/v/gen/js/builtin_types.v +++ b/vlib/v/gen/js/builtin_types.v @@ -165,8 +165,8 @@ pub fn (mut g JsGen) typ(t table.Type) string { .aggregate { panic('TODO: unhandled aggregate in JS') } - .gohandle { - panic('TODO: unhandled gohandle in JS') + .thread { + panic('TODO: unhandled thread in JS') } } /* diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index 5575d411ed..bd40981a40 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -102,16 +102,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme comments << p.eat_comments({}) mut right_comments := []ast.Comment{} mut right := []ast.Expr{cap: left.len} - if p.tok.kind == .key_go { - stmt := p.stmt(false) - go_stmt := stmt as ast.GoStmt - right << ast.GoExpr{ - go_stmt: go_stmt - pos: go_stmt.pos - } - } else { - right, right_comments = p.expr_list() - } + right, right_comments = p.expr_list() comments << right_comments end_comments := p.eat_comments(same_line: true) mut has_cross_var := false diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 61dba2b1e4..47fe9357fd 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -106,7 +106,8 @@ pub fn (mut p Parser) parse_map_type() table.Type { } pub fn (mut p Parser) parse_chan_type() table.Type { - if p.peek_tok.kind != .name && p.peek_tok.kind != .key_mut && p.peek_tok.kind != .amp { + if p.peek_tok.kind != .name && p.peek_tok.kind != .key_mut && p.peek_tok.kind != .amp + && p.peek_tok.kind != .lsbr { p.next() return table.chan_type } @@ -118,6 +119,18 @@ pub fn (mut p Parser) parse_chan_type() table.Type { return table.new_type(idx) } +pub fn (mut p Parser) parse_thread_type() table.Type { + if p.peek_tok.kind != .name && p.peek_tok.kind != .key_mut && p.peek_tok.kind != .amp + && p.peek_tok.kind != .lsbr { + p.next() + return table.thread_type + } + p.next() + elem_type := p.parse_type() + idx := p.table.find_or_register_thread(elem_type) + return table.new_type(idx) +} + pub fn (mut p Parser) parse_multi_return_type() table.Type { p.check(.lpar) mut mr_types := []table.Type{} @@ -319,6 +332,9 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool, check if name == 'chan' { return p.parse_chan_type() } + if name == 'thread' { + return p.parse_thread_type() + } defer { p.next() } diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index 3717eabe3b..9dbd1e8252 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -85,6 +85,14 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { node = p.prefix_expr() } } + .key_go { + stmt := p.stmt(false) + go_stmt := stmt as ast.GoStmt + node = ast.GoExpr{ + go_stmt: go_stmt + pos: go_stmt.pos + } + } .key_true, .key_false { node = ast.BoolLiteral{ val: p.tok.kind == .key_true diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index 78d8da7fd0..edd533e8e0 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -484,17 +484,17 @@ pub fn (t &Table) chan_cname(elem_type Type, is_mut bool) string { } [inline] -pub fn (t &Table) gohandle_name(return_type Type) string { +pub fn (t &Table) thread_name(return_type Type) string { return_type_sym := t.get_type_symbol(return_type) ptr := if return_type.is_ptr() { '&' } else { '' } - return 'gohandle[$ptr$return_type_sym.name]' + return 'thread $ptr$return_type_sym.name' } [inline] -pub fn (t &Table) gohandle_cname(return_type Type) string { +pub fn (t &Table) thread_cname(return_type Type) string { return_type_sym := t.get_type_symbol(return_type) suffix := if return_type.is_ptr() { '_ptr' } else { '' } - return 'gohandle_$return_type_sym.cname$suffix' + return '__v_thread_$return_type_sym.cname$suffix' } // map_source_name generates the original name for the v source. @@ -560,25 +560,25 @@ pub fn (mut t Table) find_or_register_map(key_type Type, value_type Type) int { return t.register_type_symbol(map_typ) } -pub fn (mut t Table) find_or_register_gohandle(return_type Type) int { - name := t.gohandle_name(return_type) - cname := t.gohandle_cname(return_type) +pub fn (mut t Table) find_or_register_thread(return_type Type) int { + name := t.thread_name(return_type) + cname := t.thread_cname(return_type) // existing existing_idx := t.type_idxs[name] if existing_idx > 0 { return existing_idx } // register - gohandle_typ := TypeSymbol{ - parent_idx: gohandle_type_idx - kind: .gohandle + thread_typ := TypeSymbol{ + parent_idx: thread_type_idx + kind: .thread name: name cname: cname - info: GoHandle{ + info: Thread{ return_type: return_type } } - return t.register_type_symbol(gohandle_typ) + return t.register_type_symbol(thread_typ) } pub fn (mut t Table) find_or_register_array(elem_type Type) int { diff --git a/vlib/v/table/types.v b/vlib/v/table/types.v index 71fd759a7a..b08cb233e7 100644 --- a/vlib/v/table/types.v +++ b/vlib/v/table/types.v @@ -16,7 +16,7 @@ import strings pub type Type = int pub type TypeInfo = Aggregate | Alias | Array | ArrayFixed | Chan | Enum | FnType | GenericStructInst | - GoHandle | Interface | Map | MultiReturn | Struct | SumType + Interface | Map | MultiReturn | Struct | SumType | Thread pub enum Language { v @@ -306,7 +306,7 @@ pub const ( any_type_idx = 25 float_literal_type_idx = 26 int_literal_type_idx = 27 - gohandle_type_idx = 28 + thread_type_idx = 28 ) pub const ( @@ -350,14 +350,14 @@ pub const ( any_type = new_type(any_type_idx) float_literal_type = new_type(float_literal_type_idx) int_literal_type = new_type(int_literal_type_idx) - gohandle_type = new_type(gohandle_type_idx) + thread_type = new_type(thread_type_idx) ) pub const ( builtin_type_names = ['void', 'voidptr', 'charptr', 'byteptr', 'i8', 'i16', 'int', 'i64', 'u16', 'u32', 'u64', 'int_literal', 'f32', 'f64', 'float_literal', 'string', 'ustring', 'char', 'byte', 'bool', 'none', 'array', 'array_fixed', 'map', 'chan', 'any', 'struct', 'mapnode', - 'size_t', 'rune', 'gohandle'] + 'size_t', 'rune', 'thread'] ) pub struct MultiReturn { @@ -419,7 +419,7 @@ pub enum Kind { float_literal int_literal aggregate - gohandle + thread } pub fn (t &TypeSymbol) str() string { @@ -471,10 +471,10 @@ pub fn (t &TypeSymbol) chan_info() Chan { } [inline] -pub fn (t &TypeSymbol) gohandle_info() GoHandle { +pub fn (t &TypeSymbol) thread_info() Thread { match mut t.info { - GoHandle { return t.info } - else { panic('TypeSymbol.gohandle_info(): no gohandle info for type: $t.name') } + Thread { return t.info } + else { panic('TypeSymbol.thread_info(): no thread info for type: $t.name') } } } @@ -540,7 +540,7 @@ pub fn (mut t Table) register_builtin_type_symbols() { cname: 'int_literal' mod: 'builtin' ) - t.register_type_symbol(kind: .gohandle, name: 'gohandle', cname: 'gohandle', mod: 'builtin') + t.register_type_symbol(kind: .thread, name: 'thread', cname: '__v_thread', mod: 'builtin') } [inline] @@ -619,7 +619,7 @@ pub fn (k Kind) str() string { .generic_struct_inst { 'generic_struct_inst' } .rune { 'rune' } .aggregate { 'aggregate' } - .gohandle { 'gohandle' } + .thread { 'thread' } } return k_str } @@ -730,7 +730,7 @@ pub mut: is_mut bool } -pub struct GoHandle { +pub struct Thread { pub mut: return_type Type } diff --git a/vlib/v/tests/go_array_wait_test.v b/vlib/v/tests/go_array_wait_test.v new file mode 100644 index 0000000000..5a5efbca15 --- /dev/null +++ b/vlib/v/tests/go_array_wait_test.v @@ -0,0 +1,38 @@ +fn f(x f64) f64 { + y := x * x + return y +} + +fn test_array_thread_f64_wait() { + mut r := []thread f64{cap: 10} + for i in 0 .. 10 { + r << go f(f64(i) + 0.5) + } + x := r.wait() + assert x == [0.25, 2.25, 6.25, 12.25, 20.25, 30.25, 42.25, 56.25, 72.25, 90.25] +} + +fn g(shared a []int, i int) { + lock a { + a[i] *= a[i] + 1 + } +} + +fn test_array_thread_void_wait() { + shared a := [2 3 5 7 11 13 17] + t := [ + go g(shared a, 0) + go g(shared a, 3) + go g(shared a, 6) + go g(shared a, 2) + go g(shared a, 1) + go g(shared a, 5) + go g(shared a, 4) + ] + println('threads started') + t.wait() + rlock a { + assert a == [6, 12, 30, 56, 132, 182, 306] + } +} +