Make Wait() async
In all of the examples, its recommended to call `Wait()` before starting
a process/task.
Since `Wait()` is a blocking call, this means it must be called from a
goroutine like so:
```go
statusC := make(chan uint32)
go func() {
status, err := task.Wait(ctx)
if err != nil {
// handle async err
}
statusC <- status
}()
task.Start(ctx)
<-statusC
```
This means there is a race here where there is no guarentee when the
goroutine is going to be scheduled, and even a bit more since this
requires an RPC call to be made.
In addition, this code is very messy and a common pattern for any caller
using Wait+Start.
Instead, this changes `Wait()` to use an async model having `Wait()`
return a channel instead of the code itself.
This ensures that when `Wait()` returns that the client has a handle on
the event stream (already made the RPC request) before returning and
reduces any sort of race to how the stream is handled by grpc since we
can't guarentee that we have a goroutine running and blocked on
`Recv()`.
Making `Wait()` async also cleans up the code in the caller drastically:
```go
statusC, err := task.Wait(ctx)
if err != nil {
return err
}
task.Start(ctx)
status := <-statusC
if status.Err != nil {
return err
}
```
No more spinning up goroutines and more natural error
handling for the caller.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
@@ -163,14 +163,10 @@ You always want to make sure you `Wait` before calling `Start` on a task.
|
||||
This makes sure that you do not encounter any races if the task has a simple program like `/bin/true` that exits promptly after calling start.
|
||||
|
||||
```go
|
||||
exitStatusC := make(chan uint32, 1)
|
||||
go func() {
|
||||
status, err := task.Wait(ctx)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
exitStatusC <- status
|
||||
}()
|
||||
exitStatusC, err := task.Wait(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := task.Start(ctx); err != nil {
|
||||
return err
|
||||
@@ -192,7 +188,10 @@ To do this we will simply call `Kill` on the task after waiting a couple of seco
|
||||
}
|
||||
|
||||
status := <-exitStatusC
|
||||
fmt.Printf("redis-server exited with status: %d\n", status)
|
||||
if status.Err != nil {
|
||||
return status.Err
|
||||
}
|
||||
fmt.Printf("redis-server exited with status: %d\n", status.Code)
|
||||
```
|
||||
|
||||
We wait on our exit status channel that we setup to ensure the task has fully exited and we get the exit status.
|
||||
@@ -271,14 +270,10 @@ func redisExample() error {
|
||||
defer task.Delete(ctx)
|
||||
|
||||
// make sure we wait before calling start
|
||||
exitStatusC := make(chan uint32, 1)
|
||||
go func() {
|
||||
status, err := task.Wait(ctx)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
exitStatusC <- status
|
||||
}()
|
||||
exitStatusC, err := task.Wait(ctx)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// call start on the task to execute the redis server
|
||||
if err := task.Start(ctx); err != nil {
|
||||
@@ -296,7 +291,10 @@ func redisExample() error {
|
||||
// wait for the process to fully exit and print out the exit status
|
||||
|
||||
status := <-exitStatusC
|
||||
fmt.Printf("redis-server exited with status: %d\n", status)
|
||||
if status.Err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("redis-server exited with status: %d\n", status.Code)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user