There are various task executors, with different properties, and some of them only support non-blocking calls. So, I was thinking, whether there's a need to use mutex/channel to safely deliver task results to calling go-routine, or whether is it enough simple WaitGroup?
For sake of simplicity, and specificity of the question, an example using very naive task executor launching function directly as go routine:
func TestRace(t *testing.T) {
    var wg sync.WaitGroup
    a, b := 1, 2
    wg.Add(1)
    // this func would be passed to real executor
    go func() {
        a, b = a+1, b+1
        wg.Done()
    }()
    wg.Wait()
    assert.Equal(t, a, 2)
    assert.Equal(t, b, 3)
}
Execution of the test above with -race option didn't fail, on my machine. However, is that enough guarantee? What if go-routine is executed on different CPU core, or on CPU core block (AMD CCX), or on different CPU in multi-socket setups?
So, the question is, can I use WaitGroup to provide synchronization (block and return values) for non-blocking executors?
 
    