Suppose I have the following function, doWork
, that starts some work in a goroutine and returns a Result
to check for completion and error:
func doWork() *Result {
r := Result{doneCh: make(chan struct{})}
go func() {
var err error
defer func() {
r.err = err
close(r.doneCh)
}()
// do some work
}()
return &r
}
where Result
is the following struct:
type Result struct {
doneCh chan struct{}
err error
}
// doneCh returns a closed chan when the work is done.
func (r *Result) Done() <-chan struct{} {
return r.doneCh
}
// Err returns a non-nil err if the work failed.
// Don't call Err until Done returns a closed chan.
func (r *Result) Err() error {
return r.err
}
is this code thread safe if I set err
before closing doneCh
:
defer func() {
r.err = err
close(r.doneCh)
}()
or is the compiler free to order the r.err = err
and close(r.doneCh)
instructions as it likes, in which case I'd need a mutex to prevent concurrent read/writes on error.
The compiler may not reorder the assignment and close statement, so you do not need a mutex if callers are well-behaved and do as instructed by your docs.
This is explained in The Go Memory Model, Channel Communication.