diff --git a/doc/docs.md b/doc/docs.md index a19bbbe1ed..4de3195608 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -2484,6 +2484,7 @@ res := ch.try_push(a) // try to perform `ch <- a` println(res) l := ch.len // number of elements in queue c := ch.cap // maximum queue length +is_closed := ch.closed // bool flag - has `ch` been closed println(l) println(c) mut b := Abc{} @@ -2495,7 +2496,10 @@ The `try_push/pop()` methods will return immediately with one of the results `.success`, `.not_ready` or `.closed` - dependent on whether the object has been transferred or the reason why not. Usage of these methods and properties in production is not recommended - -algorithms based on them are often subject to race conditions. Use `select` instead. +algorithms based on them are often subject to race conditions. Especially `.len` and +`.closed` should not be used to make decisions. +Use `or` branches, error propagation or `select` instead (see [Syntax and Usage](#syntax-and-usage) +and [Channel Select](#channel-select) above). ### Shared Objects diff --git a/vlib/sync/channel_close_test.v b/vlib/sync/channel_close_test.v index c4d45370a3..07ce9a90eb 100644 --- a/vlib/sync/channel_close_test.v +++ b/vlib/sync/channel_close_test.v @@ -1,79 +1,73 @@ import sync -fn do_rec(mut ch sync.Channel, mut resch sync.Channel) { +fn do_rec(ch chan int, resch chan i64) { mut sum := i64(0) for { - mut a := 0 - if !ch.pop(&a) { + a := <-ch or { break } sum += a } + assert ch.closed == true println(sum) - resch.push(&sum) + resch <- sum } -fn do_send(mut ch sync.Channel) { +fn do_send(ch chan int) { for i in 0 .. 8000 { - ch.push(&i) + ch <- i } + assert ch.closed == false ch.close() + assert ch.closed == true } fn test_channel_close_buffered_multi() { - mut ch := sync.new_channel(0) - mut resch := sync.new_channel(100) - go do_rec(mut ch, mut resch) - go do_rec(mut ch, mut resch) - go do_rec(mut ch, mut resch) - go do_rec(mut ch, mut resch) - go do_send(mut ch) + ch := chan int{cap: 10} + resch := chan i64{} + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_send(ch) mut sum := i64(0) for _ in 0 .. 4 { - mut r := i64(0) - resch.pop(&r) - sum += r + sum += <- resch } assert sum == i64(8000) * (8000 - 1) / 2 } fn test_channel_close_unbuffered_multi() { - mut ch := sync.new_channel(0) - mut resch := sync.new_channel(100) - go do_rec(mut ch, mut resch) - go do_rec(mut ch, mut resch) - go do_rec(mut ch, mut resch) - go do_rec(mut ch, mut resch) - go do_send(mut ch) + ch := chan int{} + resch := chan i64{} + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_send(ch) mut sum := i64(0) for _ in 0 .. 4 { - mut r := i64(0) - resch.pop(&r) - sum += r + sum += <-resch } assert sum == i64(8000) * (8000 - 1) / 2 } fn test_channel_close_buffered() { - mut ch := sync.new_channel(0) - mut resch := sync.new_channel(100) - go do_rec(mut ch, mut resch) - go do_send(mut ch) + ch := chan int{cap: 100} + resch := chan i64{} + go do_rec(ch, resch) + go do_send(ch) mut sum := i64(0) - mut r := i64(0) - resch.pop(&r) - sum += r + sum += <-resch assert sum == i64(8000) * (8000 - 1) / 2 } fn test_channel_close_unbuffered() { - mut ch := sync.new_channel(0) - mut resch := sync.new_channel(100) - go do_rec(mut ch, mut resch) - go do_send(mut ch) + ch := chan int{} + resch := chan i64{cap: 100} + go do_rec(ch, resch) + go do_send(ch) mut sum := i64(0) - mut r := i64(0) - resch.pop(&r) - sum += r + sum += <-resch assert sum == i64(8000) * (8000 - 1) / 2 } diff --git a/vlib/sync/channels.v b/vlib/sync/channels.v index bd3848d6b2..a6e4291556 100644 --- a/vlib/sync/channels.v +++ b/vlib/sync/channels.v @@ -162,6 +162,11 @@ pub fn (mut ch Channel) len() int { return int(C.atomic_load_u32(&ch.read_avail)) } +[inline] +pub fn (mut ch Channel) closed() bool { + return C.atomic_load_u16(&ch.closed) != 0 +} + [inline] pub fn (mut ch Channel) push(src voidptr) { if ch.try_push_priv(src, false) == .closed { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 7ec56ebcfa..3de4d081d2 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2080,6 +2080,10 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T selector_expr.typ = table.int_type return table.int_type } + if sym.kind == .chan && field_name == 'closed' { + selector_expr.typ = table.bool_type + return table.bool_type + } } mut unknown_field_msg := 'type `$sym.name` has no field or method `$field_name`' mut has_field := false diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 5519095fc2..1d01d8954c 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2933,8 +2933,8 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { g.write('$info.size') return } - if sym.kind == .chan && node.field_name == 'len' { - g.write('sync__Channel_len(') + if sym.kind == .chan && (node.field_name == 'len' || node.field_name == 'closed') { + g.write('sync__Channel_${node.field_name}(') g.expr(node.expr) g.write(')') return