Search code examples
gocallbackgoroutine

Goroutines, Callbacks and sync.WaitGroup


With the following code:

package main

import (
    "github.com/davecgh/go-spew/spew"
    "sync"
    "time"
)
func callbackWithTimeout(cbFunc func() ([]byte, error), timeout time.Duration) {
    defer wg.Done() //I don't want this function to know about sync.WaitGroup
    time.Sleep(timeout)
    d, e := cbFunc()
    spew.Dump(d)
    spew.Dump(e)
}
var wg sync.WaitGroup
func main() {
    wg.Add(1)
    go func() {
        cbFunc := func() ([]byte, error) {
            //I feel like I should be able to defer here instead
            return nil, nil
        }
        callbackWithTimeout(cbFunc, time.Duration(4*time.Second))
    }()
    println("some line")
    wg.Wait()
}

In function callbackWithTimeout, I don't want to use defer wg.Done() because it's not callbackWithTimeout()'s concern to wg.Done(). How do I go about implementing such a thing? i.e., remove any sync.WaitGroup in callbackWithTimeout? I have a bit of problem understanding the separation of concerns here as a callback'er function should not have to know about waitgroups but in this case it seems, I have no other choice?

I feel like it should be a caller's responsibility to wg.Done() (which in this case is the cbFunc) but lack any concise reference to documentation or ideas on how to implement it in Go because by definition, all a callback function does is call the function back. So, where I am doing it wrong?


Silly assumptions were made by yours truly during refactoring. Working code below. Many thanks.

package main

import (
    "errors"
    "github.com/davecgh/go-spew/spew"
    "sync"
    "time"
)

func callbackWithTimeout(cbFunc func() ([]byte, error), timeout time.Duration) {
    time.Sleep(timeout)
    d, e := cbFunc()
    spew.Dump(d)
    spew.Dump(e)
}

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        callbackWithTimeout(func() ([]byte, error) {
            b := []byte{1, 2, 3, 4}
            e := errors.New("error123")
            return b, e
        }, time.Duration(2*time.Second))
    }()
    println("some line")
    wg.Wait()
}

Solution

  • May be like this?

    package main
    
    import (
        "sync"
        "time"
    
        "github.com/davecgh/go-spew/spew"
    )
    
    func callbackWithTimeout(cbFunc func() ([]byte, error), timeout time.Duration) {
        time.Sleep(timeout)
        d, e := cbFunc()
        spew.Dump(d)
        spew.Dump(e)
    }
    
    func main() {
        var wg sync.WaitGroup
    
        wg.Add(1)
    
        go func() {
            defer wg.Done() // move it here
            cbFunc := func() ([]byte, error) {
                //I feel like I should be able to defer here instead
                return nil, nil
            }
            callbackWithTimeout(cbFunc, time.Duration(4*time.Second))
        }()
    
        println("some line")
    
        wg.Wait()
    }