cgen: implement channel `select` block code generation (#6424)
parent
5782f562ae
commit
31a52cdf21
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue