sync/channels: provide `.cap` and `.len()` (#6104)
parent
bc3b411b12
commit
433610b5c0
|
@ -250,6 +250,15 @@ if ch.pop(&m) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
There are also methods `try_push()` and `try_pop()` which return immediatelly with the return value `.not_ready` if the transaction
|
||||||
|
cannot be performed without waiting. The return value is of type `sync.TransactionState` which can also be
|
||||||
|
`.success` or `.closed`.
|
||||||
|
|
||||||
|
To monitor a channel there is a method `len()` which returns the number of elements currently in the queue and the attribute
|
||||||
|
`cap` for the queue length. Please be aware that in general `channel.len() > 0` does not guarantee that the next
|
||||||
|
`pop()` will succeed without waiting, since other threads may already have "stolen" elements from the queue. Use `try_pop()` to
|
||||||
|
accomplish this kind of task.
|
||||||
|
|
||||||
The select call is somewhat tricky. The `channel_select()` function needs three arrays that
|
The select call is somewhat tricky. The `channel_select()` function needs three arrays that
|
||||||
contain the channels, the directions (pop/push) and the object references and
|
contain the channels, the directions (pop/push) and the object references and
|
||||||
a timeout of type `time.Duration` (`time.infinite` or `-1` to wait unlimited) as parameters. It returns the
|
a timeout of type `time.Duration` (`time.infinite` or `-1` to wait unlimited) as parameters. It returns the
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import sync
|
||||||
|
|
||||||
|
const (
|
||||||
|
queue_len = 1000
|
||||||
|
queue_fill = 763
|
||||||
|
)
|
||||||
|
|
||||||
|
fn do_send(mut ch sync.Channel, fin sync.Semaphore) {
|
||||||
|
for i in 0 .. queue_fill {
|
||||||
|
ch.push(&i)
|
||||||
|
}
|
||||||
|
fin.post()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_channel_len_cap() {
|
||||||
|
mut ch := sync.new_channel<int>(queue_len)
|
||||||
|
sem := sync.new_semaphore()
|
||||||
|
go do_send(mut ch, sem)
|
||||||
|
sem.wait()
|
||||||
|
assert ch.cap == queue_len
|
||||||
|
assert ch.len() == queue_fill
|
||||||
|
}
|
|
@ -90,7 +90,6 @@ struct Channel {
|
||||||
ringbuf byteptr // queue for buffered channels
|
ringbuf byteptr // queue for buffered channels
|
||||||
statusbuf byteptr // flags to synchronize write/read in ringbuf
|
statusbuf byteptr // flags to synchronize write/read in ringbuf
|
||||||
objsize u32
|
objsize u32
|
||||||
queue_length u32 // in #objects
|
|
||||||
mut: // atomic
|
mut: // atomic
|
||||||
write_adr C.atomic_uintptr_t // if != NULL the next obj can be written here without wait
|
write_adr C.atomic_uintptr_t // if != NULL the next obj can be written here without wait
|
||||||
read_adr C.atomic_uintptr_t // if != NULL an obj can be read from here without wait
|
read_adr C.atomic_uintptr_t // if != NULL an obj can be read from here without wait
|
||||||
|
@ -106,6 +105,8 @@ mut: // atomic
|
||||||
write_sub_mtx u16
|
write_sub_mtx u16
|
||||||
read_sub_mtx u16
|
read_sub_mtx u16
|
||||||
closed u16
|
closed u16
|
||||||
|
pub:
|
||||||
|
cap u32 // queue length in #objects
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_channel<T>(n u32) &Channel {
|
pub fn new_channel<T>(n u32) &Channel {
|
||||||
|
@ -116,7 +117,7 @@ pub fn new_channel<T>(n u32) &Channel {
|
||||||
writesem_im: new_semaphore()
|
writesem_im: new_semaphore()
|
||||||
readsem_im: new_semaphore()
|
readsem_im: new_semaphore()
|
||||||
objsize: st
|
objsize: st
|
||||||
queue_length: n
|
cap: n
|
||||||
write_free: n
|
write_free: n
|
||||||
read_avail: 0
|
read_avail: 0
|
||||||
ringbuf: if n > 0 { malloc(int(n * st)) } else { byteptr(0) }
|
ringbuf: if n > 0 { malloc(int(n * st)) } else { byteptr(0) }
|
||||||
|
@ -152,6 +153,11 @@ pub fn (mut ch Channel) close() {
|
||||||
C.atomic_store_u16(&ch.write_sub_mtx, u16(0))
|
C.atomic_store_u16(&ch.write_sub_mtx, u16(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[inline]
|
||||||
|
pub fn (mut ch Channel) len() int {
|
||||||
|
return int(C.atomic_load_u32(&ch.read_avail))
|
||||||
|
}
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
pub fn (mut ch Channel) push(src voidptr) {
|
pub fn (mut ch Channel) push(src voidptr) {
|
||||||
if ch.try_push_priv(src, false) == .closed {
|
if ch.try_push_priv(src, false) == .closed {
|
||||||
|
@ -188,7 +194,7 @@ fn (mut ch Channel) try_push_priv(src voidptr, no_block bool) TransactionState {
|
||||||
return .success
|
return .success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if no_block && ch.queue_length == 0 {
|
if no_block && ch.cap == 0 {
|
||||||
return .not_ready
|
return .not_ready
|
||||||
}
|
}
|
||||||
// get token to read
|
// get token to read
|
||||||
|
@ -201,7 +207,7 @@ fn (mut ch Channel) try_push_priv(src voidptr, no_block bool) TransactionState {
|
||||||
if !got_sem {
|
if !got_sem {
|
||||||
ch.writesem.wait()
|
ch.writesem.wait()
|
||||||
}
|
}
|
||||||
if ch.queue_length == 0 {
|
if ch.cap == 0 {
|
||||||
// try to advertise current object as readable
|
// try to advertise current object as readable
|
||||||
mut read_in_progress := false
|
mut read_in_progress := false
|
||||||
C.atomic_store_ptr(&ch.read_adr, src)
|
C.atomic_store_ptr(&ch.read_adr, src)
|
||||||
|
@ -275,8 +281,8 @@ fn (mut ch Channel) try_push_priv(src voidptr, no_block bool) TransactionState {
|
||||||
mut wr_idx := C.atomic_load_u32(&ch.buf_elem_write_idx)
|
mut wr_idx := C.atomic_load_u32(&ch.buf_elem_write_idx)
|
||||||
for {
|
for {
|
||||||
mut new_wr_idx := wr_idx + 1
|
mut new_wr_idx := wr_idx + 1
|
||||||
for new_wr_idx >= ch.queue_length {
|
for new_wr_idx >= ch.cap {
|
||||||
new_wr_idx -= ch.queue_length
|
new_wr_idx -= ch.cap
|
||||||
}
|
}
|
||||||
if C.atomic_compare_exchange_strong_u32(&ch.buf_elem_write_idx, &wr_idx, new_wr_idx) {
|
if C.atomic_compare_exchange_strong_u32(&ch.buf_elem_write_idx, &wr_idx, new_wr_idx) {
|
||||||
break
|
break
|
||||||
|
@ -335,7 +341,7 @@ fn (mut ch Channel) try_pop_priv(dest voidptr, no_block bool) TransactionState {
|
||||||
mut write_in_progress := false
|
mut write_in_progress := false
|
||||||
for {
|
for {
|
||||||
mut got_sem := false
|
mut got_sem := false
|
||||||
if ch.queue_length == 0 {
|
if ch.cap == 0 {
|
||||||
// unbuffered channel - first see if a `push()` has adversized
|
// unbuffered channel - first see if a `push()` has adversized
|
||||||
mut rdadr := C.atomic_load_ptr(&ch.read_adr)
|
mut rdadr := C.atomic_load_ptr(&ch.read_adr)
|
||||||
for rdadr != C.NULL {
|
for rdadr != C.NULL {
|
||||||
|
@ -367,7 +373,7 @@ fn (mut ch Channel) try_pop_priv(dest voidptr, no_block bool) TransactionState {
|
||||||
}
|
}
|
||||||
ch.readsem.wait()
|
ch.readsem.wait()
|
||||||
}
|
}
|
||||||
if ch.queue_length > 0 {
|
if ch.cap > 0 {
|
||||||
// try to get buffer token
|
// try to get buffer token
|
||||||
mut obj_in_queue := false
|
mut obj_in_queue := false
|
||||||
mut rd_avail := C.atomic_load_u32(&ch.read_avail)
|
mut rd_avail := C.atomic_load_u32(&ch.read_avail)
|
||||||
|
@ -381,8 +387,8 @@ fn (mut ch Channel) try_pop_priv(dest voidptr, no_block bool) TransactionState {
|
||||||
mut rd_idx := C.atomic_load_u32(&ch.buf_elem_read_idx)
|
mut rd_idx := C.atomic_load_u32(&ch.buf_elem_read_idx)
|
||||||
for {
|
for {
|
||||||
mut new_rd_idx := rd_idx + 1
|
mut new_rd_idx := rd_idx + 1
|
||||||
for new_rd_idx >= ch.queue_length {
|
for new_rd_idx >= ch.cap {
|
||||||
new_rd_idx -= ch.queue_length
|
new_rd_idx -= ch.cap
|
||||||
}
|
}
|
||||||
if C.atomic_compare_exchange_weak_u32(&ch.buf_elem_read_idx, &rd_idx, new_rd_idx) {
|
if C.atomic_compare_exchange_weak_u32(&ch.buf_elem_read_idx, &rd_idx, new_rd_idx) {
|
||||||
break
|
break
|
||||||
|
@ -419,7 +425,7 @@ fn (mut ch Channel) try_pop_priv(dest voidptr, no_block bool) TransactionState {
|
||||||
}
|
}
|
||||||
// try to advertise `dest` as writable
|
// try to advertise `dest` as writable
|
||||||
C.atomic_store_ptr(&ch.write_adr, dest)
|
C.atomic_store_ptr(&ch.write_adr, dest)
|
||||||
if ch.queue_length == 0 {
|
if ch.cap == 0 {
|
||||||
mut rdadr := C.atomic_load_ptr(&ch.read_adr)
|
mut rdadr := C.atomic_load_ptr(&ch.read_adr)
|
||||||
if rdadr != C.NULL {
|
if rdadr != C.NULL {
|
||||||
mut dest2 := dest
|
mut dest2 := dest
|
||||||
|
@ -431,7 +437,7 @@ fn (mut ch Channel) try_pop_priv(dest voidptr, no_block bool) TransactionState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ch.queue_length == 0 && !write_in_progress {
|
if ch.cap == 0 && !write_in_progress {
|
||||||
mut null16 := u16(0)
|
mut null16 := u16(0)
|
||||||
for !C.atomic_compare_exchange_weak_u16(&ch.write_sub_mtx, &null16, u16(1)) {
|
for !C.atomic_compare_exchange_weak_u16(&ch.write_sub_mtx, &null16, u16(1)) {
|
||||||
null16 = u16(0)
|
null16 = u16(0)
|
||||||
|
|
Loading…
Reference in New Issue