From 31a52cdf2121fbaf9eeb8e2684f26baf5757cdb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kr=C3=BCger?= <45282134+UweKrueger@users.noreply.github.com> Date: Sat, 19 Sep 2020 02:14:35 +0200 Subject: [PATCH] cgen: implement channel `select` block code generation (#6424) --- vlib/sync/channel_select_3_test.v | 97 +++++++++++++++++++++ vlib/v/ast/ast.v | 2 +- vlib/v/checker/checker.v | 13 ++- vlib/v/gen/cgen.v | 138 +++++++++++++++++++++++++++++- vlib/v/parser/if_match.v | 1 + 5 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 vlib/sync/channel_select_3_test.v diff --git a/vlib/sync/channel_select_3_test.v b/vlib/sync/channel_select_3_test.v new file mode 100644 index 0000000000..b679cb9fd7 --- /dev/null +++ b/vlib/sync/channel_select_3_test.v @@ -0,0 +1,97 @@ +import time +import sync + +struct St { + a int +} + +fn f1(ch1 chan int, ch2 chan St, ch3 chan int, sem sync.Semaphore) { + mut a := 5 + st := St{} + select { + a = <-ch3 { + a = 0 + } + b := <-ch2 { + a = b.a + } + ch2 <- st { + a = 2 + } + > 300 * time.millisecond { + a = 3 + } + } + assert a == 3 + sem.post() +} + +fn f2(ch1 chan St, ch2 chan int, sem sync.Semaphore) { + mut r := 23 + for i in 0 .. 2 { + select { + b := <- ch1 { + r = b.a + } + ch2 <- r { + r = 17 + } + } + if i == 0 { + assert r == 17 + } else { + assert r == 13 + } + } + sem.post() +} + +fn test_select_blocks() { + ch1 := chan int{cap: 1} + ch2 := chan St{} + ch3 := chan int{} + sem := sync.new_semaphore() + mut r := false + t := select { + b := <- ch1 { + println(b) + } + else { + // no channel ready + r = true + } + } + assert r == true + assert t == true + go f2(ch2, ch3, sem) + n := <-ch3 + assert n == 23 + ch2 <- St{a: 13} + sem.wait() + stopwatch := time.new_stopwatch({}) + go f1(ch1, ch2, ch3, sem) + sem.wait() + elapsed_ms := f64(stopwatch.elapsed()) / time.millisecond + assert elapsed_ms >= 295.0 + + ch1.close() + ch2.close() + mut h := 7 + u := select { + b := <- ch2 { + h = 0 + } + ch1 <- h { + h = 1 + } + else { + h = 2 + } + } + // no branch should have run + assert h == 7 + // since all channels are closed `select` should return `false` + assert u == false +} + + diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 240c102fb7..e3bffc9ca7 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -536,9 +536,9 @@ pub struct SelectExpr { pub: branches []SelectBranch pos token.Position + has_exception bool pub mut: is_expr bool // returns a value - return_type table.Type expected_type table.Type // for debugging only } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 441a5f473a..89b382f28b 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2656,8 +2656,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { return table.void_type } ast.SelectExpr { - // TODO: to be implemented - return table.void_type + return c.select_expr(mut node) } ast.SelectorExpr { return c.selector_expr(mut node) @@ -3060,6 +3059,16 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol c.error(err_details, node.pos) } +pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) table.Type { + node.is_expr = c.expected_type != table.void_type + node.expected_type = c.expected_type + for branch in node.branches { + c.stmt(branch.stmt) + c.stmts(branch.stmts) + } + return table.bool_type +} + pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type { for id in node.lockeds { c.ident(mut id) diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index ade16eaecd..9cbde9a0a9 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2129,7 +2129,7 @@ fn (mut g Gen) expr(node ast.Expr) { // Only used in IndexExpr } ast.SelectExpr { - // TODO: to be implemented + g.select_expr(node) } ast.SizeOf { if node.is_type { @@ -2739,6 +2739,142 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) { } } +fn (mut g Gen) select_expr(node ast.SelectExpr) { + is_expr := node.is_expr || g.inside_ternary > 0 + cur_line := if is_expr { + g.empty_line = true + g.go_before_stmt(0) + } else { + '' + } + n_channels := if node.has_exception { node.branches.len - 1 } else { node.branches.len } + mut channels := []ast.Expr{cap: n_channels} + mut objs := []ast.Expr{cap: n_channels} + mut tmp_objs := []string{cap: n_channels} + mut elem_types := []string{cap: n_channels} + mut is_push := []bool{cap: n_channels} + mut has_else := false + mut has_timeout := false + mut timeout_expr := ast.Expr{} + mut exception_branch := -1 + for j, branch in node.branches { + if branch.is_else { + has_else = true + exception_branch = j + } else if branch.is_timeout { + has_timeout = true + exception_branch = j + timeout_expr = (branch.stmt as ast.ExprStmt).expr + } else { + match branch.stmt as stmt { + ast.ExprStmt { + expr := stmt.expr as ast.InfixExpr + channels << expr.left + objs << expr.right + tmp_objs << '' + elem_types << '' + is_push << true + } + ast.AssignStmt { + rec_expr := stmt.right[0] as ast.PrefixExpr + channels << rec_expr.right + is_push << false + // create tmp unless the object with *exactly* the type we need exists already + if stmt.op == .decl_assign || stmt.right_types[0] != stmt.left_types[0] { + tmp_obj := g.new_tmp_var() + tmp_objs << tmp_obj + el_stype := g.typ(stmt.right_types[0]) + elem_types << if stmt.op == .decl_assign { + el_stype + ' ' + } else { + '' + } + g.writeln('$el_stype $tmp_obj;') + } else { + tmp_objs << '' + elem_types << '' + } + objs << stmt.left[0] + } + else {} + } + } + } + chan_array := g.new_tmp_var() + g.write('array_sync__Channel_ptr $chan_array = new_array_from_c_array($n_channels, $n_channels, sizeof(sync__Channel*), _MOV((sync__Channel*[$n_channels]){') + for i in 0 .. n_channels { + if i > 0 { + g.write(', ') + } + g.write('(sync__Channel*)(') + g.expr(channels[i]) + g.write(')') + } + g.writeln('}));') + directions_array := g.new_tmp_var() + g.write('array_sync__Direction $directions_array = new_array_from_c_array($n_channels, $n_channels, sizeof(sync__Direction), _MOV((sync__Direction[$n_channels]){') + for i in 0 .. n_channels { + if i > 0 { + g.write(', ') + } + if is_push[i] { + g.write('sync__Direction_push') + } else { + g.write('sync__Direction_pop') + } + } + g.writeln('}));') + objs_array := g.new_tmp_var() + g.write('array_voidptr $objs_array = new_array_from_c_array($n_channels, $n_channels, sizeof(voidptr), _MOV((voidptr[$n_channels]){') + for i in 0 .. n_channels { + g.write(if i > 0 { + ', &' + } else { + '&' + }) + if tmp_objs[i] == '' { + g.expr(objs[i]) + } else { + g.write(tmp_objs[i]) + } + } + g.writeln('}));') + select_result := g.new_tmp_var() + g.write('int $select_result = sync__channel_select(&/*arr*/$chan_array, $directions_array, &/*arr*/$objs_array, ') + if has_timeout { + g.expr(timeout_expr) + } else if has_else { + g.write('0') + } else { + g.write('-1') + } + g.writeln(');') + mut i := 0 + for j in 0 .. node.branches.len { + if j > 0 { + g.write('} else ') + } + g.write('if ($select_result == ') + if j == exception_branch { + g.writeln('-1) {') + } else { + g.writeln('$i) {') + if tmp_objs[i] != '' { + g.write('\t${elem_types[i]}') + g.expr(objs[i]) + g.writeln(' = ${tmp_objs[i]};') + } + i++ + } + g.stmts(node.branches[j].stmts) + } + g.writeln('}') + if is_expr { + g.write(cur_line) + g.write('($select_result != -2)') + } +} + fn (mut g Gen) ident(node ast.Ident) { if node.name == 'lld' { return diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index b97523bb9b..ca51bc0d27 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -442,5 +442,6 @@ fn (mut p Parser) select_expr() ast.SelectExpr { return ast.SelectExpr{ branches: branches pos: pos + has_exception: has_else || has_timeout } }