Skip to content

Commit

Permalink
context - illustrate cancellation better
Browse files Browse the repository at this point in the history
  • Loading branch information
quii committed Feb 14, 2019
1 parent db9c66c commit 7e7ca27
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 9 deletions.
26 changes: 21 additions & 5 deletions context.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,20 +308,36 @@ func (s *SpyStore) Fetch(ctx context.Context) (string, error) {
data := make(chan string, 1)

go func() {
time.Sleep(100 * time.Millisecond)
data <- s.response
var result string
for _, c := range s.response {
select {
case <-ctx.Done():
s.t.Log("spy store got cancelled")
return
default:
time.Sleep(10 * time.Millisecond)
result += string(c)
}
}
data <- result
}()

select {
case msg := <-data:
return msg, nil
case <-ctx.Done():
return "", ctx.Err()
case res := <-data:
return res, nil
}
}
```

We have to make our spy act like a real method that works with `context`. It's similar to our approach from before, we use Go's concurrency primitives to make two asynchronous processes race each other to determine what we return.
We have to make our spy act like a real method that works with `context`.

We are simulating a slow process where we build the result slowly by appending the string, character by character in a goroutine. When the goroutine finishes its work it writes the string to the `data` channel. The goroutine listens for the `ctx.Done` and will stop the work if a signal is sent in that channel.

Finally the code uses another `select` to wait for that goroutine to finish its work or for the cancellation to occur.

It's similar to our approach from before, we use Go's concurrency primitives to make two asynchronous processes race each other to determine what we return.

You'll take a similar approach when writing your own functions and methods that accept a `context` so make sure you understand what's going on.

Expand Down
3 changes: 3 additions & 0 deletions context/v3/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1

import (
"context"
"go.uber.org/goleak"
"net/http"
"net/http/httptest"
"testing"
Expand All @@ -12,6 +13,7 @@ func TestServer(t *testing.T) {
data := "hello, world"

t.Run("returns data from store", func(t *testing.T) {
defer goleak.VerifyNone(t)
store := &SpyStore{response: data, t: t}
svr := Server(store)

Expand All @@ -26,6 +28,7 @@ func TestServer(t *testing.T) {
})

t.Run("tells store to cancel work if request is cancelled", func(t *testing.T) {
defer goleak.VerifyNone(t)
store := &SpyStore{response: data, t: t}
svr := Server(store)

Expand Down
18 changes: 14 additions & 4 deletions context/v3/testdoubles.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,25 @@ func (s *SpyStore) Fetch(ctx context.Context) (string, error) {
data := make(chan string, 1)

go func() {
time.Sleep(100 * time.Millisecond)
data <- s.response
var result string
for _, c := range s.response {
select {
case <-ctx.Done():
s.t.Log("spy store got cancelled")
return
default:
time.Sleep(10 * time.Millisecond)
result += string(c)
}
}
data <- result
}()

select {
case msg := <-data:
return msg, nil
case <-ctx.Done():
return "", ctx.Err()
case res := <-data:
return res, nil
}
}

Expand Down

0 comments on commit 7e7ca27

Please sign in to comment.