this code sometimes hangs

173 views
Skip to first unread message

Lammie Jonson

unread,
Jul 18, 2024, 10:24:53 AM (3 days ago) Jul 18
to golang-nuts
// I am not sure why this code sometimes deadlocks, but sometimes it doesn't.
// Hopefully someone will have some suggestions 

package main

// ??? sometimes deadlocks

import (
    "fmt"
    "sync"
    "time"
)

type Button struct {
    Clicked *sync.Cond
}

func main() {
    button := Button{
        Clicked: sync.NewCond(&sync.Mutex{}),
    }

    // running on goroutine every function that passed/registered
    // and wait, not exit until that goroutine is confirmed to be running
    subscribe := func(c *sync.Cond, param string, fn func(s string)) {
        var goroutineRunning sync.WaitGroup
        var cnt = 0
        cnt = cnt + 1

        fmt.Println("cnt:", cnt)
        goroutineRunning.Add(1)

        go func(p string) {
          goroutineRunning.Done()
          c.L.Lock() // critical section
          defer c.L.Unlock()

          fmt.Println("Registered and wait ... ")
          // HANG SPOT: can hang here
          c.Wait()
          fmt.Println("afer goroutine wait for:", param)

          fn(p)
        }(param)
        fmt.Println("call wait before goroutine exit")
        goroutineRunning.Wait()
        fmt.Println("after wait, subscribe exits")
    }

    var clickRegistered sync.WaitGroup

    for _, v := range []string{
        "Maximizing window.",
        "Displaying annoying dialog box!",
        "Mouse clicked."} {

        clickRegistered.Add(1)

        subscribe(button.Clicked, v, func(s string) {
            fmt.Println(s, "****************************")
            clickRegistered.Done()
        })
    }

    fmt.Println("broadcast")
    // is supposed to get the goroutines past HANG SPOT
    button.Clicked.Broadcast()
    time.Sleep(4000 * time.Millisecond)

    clickRegistered.Wait()
}

Jan Mercl

unread,
Jul 18, 2024, 11:15:58 AM (3 days ago) Jul 18
to Lammie Jonson, golang-nuts
On Thu, Jul 18, 2024 at 4:24 PM Lammie Jonson <jrub...@gmail.com> wrote:
> goroutineRunning.Done()

Should be

defer goroutineRunning.Done()

(not tested)

robert engels

unread,
Jul 18, 2024, 11:21:17 AM (3 days ago) Jul 18
to Lammie Jonson, golang-nuts
Because the wait on the condition is in another Go routine, so the the Broadcast occurs before all of the routines are waiting. Broadcast only wakes up currently waiting routines.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/d8148ef1-4d56-4b0b-a2d8-2992abe7a6c2n%40googlegroups.com.

robert engels

unread,
Jul 18, 2024, 11:24:23 AM (3 days ago) Jul 18
to Lammie Jonson, golang-nuts
To clarify, the inner Go routine calls Done() at the start, so the main can get to the Broadcast before the inners are waiting.

Which is what Jan’s fix will fix :) (pretty sure)

Lammie Jonson

unread,
Jul 18, 2024, 4:01:30 PM (3 days ago) Jul 18
to golang-nuts

>> defer goroutineRunning.Done()

This doesn't work. Each invocation of subscribe() has it's own sync.WaitGroup. It's just used it appears to ensure that the enclosed goroutine is running before subscribe() returns 

Robert Engels

unread,
Jul 18, 2024, 4:07:26 PM (3 days ago) Jul 18
to Lammie Jonson, golang-nuts
But I think that Done() is for the outer which prevents the broadcast. By putting it in the defer all of the subscribers should be waiting on the condition when it occurs. 

Caveat- not looking at code - just remembering. 

On Jul 18, 2024, at 3:01 PM, Lammie Jonson <jrub...@gmail.com> wrote:



>> defer goroutineRunning.Done()

This doesn't work. Each invocation of subscribe() has it's own sync.WaitGroup. It's just used it appears to ensure that the enclosed goroutine is running before subscribe() returns 

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

王旭东

unread,
Jul 18, 2024, 9:33:57 PM (3 days ago) Jul 18
to golang-nuts
Hi,
It’s better to not use sync.Cond. Instead, use a channel to do a jame job. There is even a issue to discuss remove it.

Laurence Guild

unread,
Jul 19, 2024, 4:52:57 PM (2 days ago) Jul 19
to golang-nuts

 Is there an article that says not to use sync.Cond ? This page seems to have some fans of sync.Cond. I have xtensive software experience but I am new to golang so I am just trying to learn

Ian Lance Taylor

unread,
Jul 19, 2024, 4:58:13 PM (2 days ago) Jul 19
to Laurence Guild, golang-nuts
On Fri, Jul 19, 2024 at 1:52 PM Laurence Guild <massr...@gmail.com> wrote:
>
>
> Is there an article that says not to use sync.Cond ? This page seems to have some fans of sync.Cond. I have xtensive software experience but I am new to golang so I am just trying to learn
>
> https://github.com/golang/go/issues/21165

I would say that sync.Cond has its uses in Go, but they are rare. In
Go one normally uses channels for the kinds of things for which other
languages use conditional variables. It's probably a mistake to use
sync.Cond without a clear understanding of why channels won't work.

Ian

Laurence Guild

unread,
Jul 19, 2024, 5:05:49 PM (2 days ago) Jul 19
to golang-nuts
well, if I am trying to learn golang then I may need examples of how to use sync.Cond, but it's good to know that it may have problems. If you have an interview then people may ask you questions and if you haven't tried to use some feature or haven't played with it then you may feel like you don't fully understand things and it can effect your feeling of confidence 

Kurtis Rader

unread,
Jul 19, 2024, 5:16:18 PM (2 days ago) Jul 19
to Laurence Guild, golang-nuts
On Fri, Jul 19, 2024 at 2:05 PM Laurence Guild <massr...@gmail.com> wrote:
well, if I am trying to learn golang then I may need examples of how to use sync.Cond, but it's good to know that it may have problems. If you have an interview then people may ask you questions and if you haven't tried to use some feature or haven't played with it then you may feel like you don't fully understand things and it can effect your feeling of confidence 

It's not that using sync.Cond "may have problems." Used correctly a sync.Cond works fine in Go. It's that, as Ian said, sync.Cond is rarely needed in Go programs and using a Go channel for transferring ownership of data is idiomatic Go. So you should consider whether a channel will work and only fall back on low level primitives like sync.Cond if a channel isn't suitable.

--
Kurtis Rader
Caretaker of the exceptional canines Junior and Hank

Brian Candler

unread,
Jul 20, 2024, 4:33:05 AM (yesterday) Jul 20
to golang-nuts
This video is well worth watching, pausing and rewinding as required. It's Bryan C. Mills "Rethinking Classical Concurrency Patterns":

It shows patterns which are tricky to implement correctly with condition variables and semaphores can be cleanly implemented with channels. It also shows a Go replacement for the traditional "worker pool" pattern.
Reply all
Reply to author
Forward
0 new messages