sync/channels: expose `ch.closed` as `bool` pseudo attribute (#8244)

pull/8092/head
Uwe Krüger 2021-01-22 08:37:29 +01:00 committed by GitHub
parent 820e684313
commit 925ffd76f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 43 deletions

View File

@ -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

View File

@ -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<int>(0)
mut resch := sync.new_channel<i64>(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<int>(0)
mut resch := sync.new_channel<i64>(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<int>(0)
mut resch := sync.new_channel<i64>(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<int>(0)
mut resch := sync.new_channel<i64>(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
}

View File

@ -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 {

View File

@ -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

View File

@ -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