Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waiting on a sync.Cond with a timeout

Tags:

go

sync

Is it possible in some easy way to do the equivalent of Java's

wait(long timeMillis)

which waits on a monitor (mutex+cond, roughly) for a specified amount of time, and returns if it is not signalled?

I can't find anything in the docs or googling around on this, and although it's of course possible to play some games with making a WaitGroup and having a timer goroutine pop, that seems tedious/annoying/inefficient to just get this simple functionality (which is by the way directly supported by any underlying systems thread library I've ever encountered)

Edit: Yes we have all read http://www.golang-book.com/10/index.htm as well as https://blog.golang.org/pipelines - again, creating more threads is a "bad" (non-performant) solution to this, and channels are also not well suited to this. Imagine for a use case a typical concurrent server Join() method... (Please do not tell me to invert the control and use a Listener pattern instead. You don't always have the luxury to change the API you are working with...)

like image 333
BadZen Avatar asked Apr 28 '15 15:04

BadZen


2 Answers

I sketched out a couple of possible alternatives in my GopherCon talk this year (see https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view). The “Condition Variables” section starts at slide 37, but this particular pattern is covered in more detail in the backup slides (101-105).

As zviadm notes, one option (https://play.golang.org/p/tWVvXOs87HX) is to close a channel.

Another option (https://play.golang.org/p/uRwV_i0v13T) is to have each waiter allocate a 1-buffered channel, and have the broadcaster send a token into the buffer to broadcast.

If the event is a persistent condition, such as “the queue is empty”, a third option (https://play.golang.org/p/uvx8vFSQ2f0) is to use a 1-buffered channel and have each receiver refill the buffer as long as the condition persists.

like image 82
bcmills Avatar answered Sep 19 '22 05:09

bcmills


https://gitlab.com/jonas.jasas/condchan makes it possible to timeout on wait. Please see an example:

package main

import (
    "fmt"
    "sync"
    "time"
    "gitlab.com/jonas.jasas/condchan"
)

func main() {
    cc := condchan.New(&sync.Mutex{})
    timeoutChan := time.After(time.Second)

    cc.L.Lock()
    // Passing func that gets channel c that signals when
    // Signal or Broadcast is called on CondChan
    cc.Select(func(c <-chan struct{}) { // Waiting with select
        select {
        case <-c: // Never ending wait
        case <-timeoutChan:
            fmt.Println("Hooray! Just escaped from eternal wait.")
        }
    })
    cc.L.Unlock()
}
like image 29
Jonas Avatar answered Sep 18 '22 05:09

Jonas