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:
|
pub:
|
||||||
branches []SelectBranch
|
branches []SelectBranch
|
||||||
pos token.Position
|
pos token.Position
|
||||||
|
has_exception bool
|
||||||
pub mut:
|
pub mut:
|
||||||
is_expr bool // returns a value
|
is_expr bool // returns a value
|
||||||
return_type table.Type
|
|
||||||
expected_type table.Type // for debugging only
|
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
|
return table.void_type
|
||||||
}
|
}
|
||||||
ast.SelectExpr {
|
ast.SelectExpr {
|
||||||
// TODO: to be implemented
|
return c.select_expr(mut node)
|
||||||
return table.void_type
|
|
||||||
}
|
}
|
||||||
ast.SelectorExpr {
|
ast.SelectorExpr {
|
||||||
return c.selector_expr(mut node)
|
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)
|
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 {
|
pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type {
|
||||||
for id in node.lockeds {
|
for id in node.lockeds {
|
||||||
c.ident(mut id)
|
c.ident(mut id)
|
||||||
|
|
|
@ -2129,7 +2129,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
// Only used in IndexExpr
|
// Only used in IndexExpr
|
||||||
}
|
}
|
||||||
ast.SelectExpr {
|
ast.SelectExpr {
|
||||||
// TODO: to be implemented
|
g.select_expr(node)
|
||||||
}
|
}
|
||||||
ast.SizeOf {
|
ast.SizeOf {
|
||||||
if node.is_type {
|
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) {
|
fn (mut g Gen) ident(node ast.Ident) {
|
||||||
if node.name == 'lld' {
|
if node.name == 'lld' {
|
||||||
return
|
return
|
||||||
|
|
|
@ -442,5 +442,6 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
|
||||||
return ast.SelectExpr{
|
return ast.SelectExpr{
|
||||||
branches: branches
|
branches: branches
|
||||||
pos: pos
|
pos: pos
|
||||||
|
has_exception: has_else || has_timeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue