You have no guarantee to observe changes made to the value of a variable in another goroutine without synchronization. See The Go Memory Model for details.
So if you want to change ctx.Response.Status
in another goroutine, for this change to be guaranteed to be visible in the caller goroutine use synchronization.
There are multiple synchronization primitives. You can use channels or the sync
package.
Using channels:
ch := make(chan int)
go func() {
err = l.Save(file)
if err != nil {
ctx.Response.Status = 500
ctx.Response.Body = err
} else {
ctx.Response.Status = 204
}
ch <- 0 // Signal that ctx is updated
// goroutine may do other works (not related to changing ctx)
}()
<- ch // Wait for the goroutine to finish updating ctx
var wg sync.WaitGroup
wg.Add(1)
go func() {
err = l.Save(file)
if err != nil {
ctx.Response.Status = 500
ctx.Response.Body = err
} else {
ctx.Response.Status = 204
}
wg.Done() // Signal that ctx is updated
// goroutine may do other works (not related to changing ctx)
}()
wg.Wait() // Wait for the goroutine to finish updating ctx
m := sync.Mutex{}
m.Lock()
go func() {
err = l.Save(file)
if err != nil {
ctx.Response.Status = 500
ctx.Response.Body = err
} else {
ctx.Response.Status = 204
}
m.Unlock() // Signal that ctx is updated
// goroutine may do other works (not related to changing ctx)
}()
m.Lock() // Wait for the goroutine to finish updating ctx
Note:
It is good practice to signal the completion (ctx update in your case) using defer
so that if the started goroutine would end in some unexpected way (e.g. runtime panic), the caller goroutine would not get blocked forever. Note that however in this case the completion signal will only be sent at the end of the anonymous function (that's when deferred functions are executed).