Skip to content

Commit

Permalink
Merge pull request #47 from k1LoW/change-cancel
Browse files Browse the repository at this point in the history
Change the behaviour of donegroup.Cancel significantly.
  • Loading branch information
k1LoW authored Jun 4, 2024
2 parents d8095c1 + e893a51 commit c4778f1
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 122 deletions.
22 changes: 7 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,29 +255,21 @@ donegroup.Go(ctx, func() error {

Also, with [donegroup.Go](https://pkg.go.dev/github.com/k1LoW/donegroup#Go), the error can be received via [donegroup.Wait](https://pkg.go.dev/github.com/k1LoW/donegroup#Wait).

### [donegroup.Cancel](https://pkg.go.dev/github.com/k1LoW/donegroup#Cancel) ( Syntax sugar for `cancel()` and donegroup.Wait )
### [donegroup.Cancel](https://pkg.go.dev/github.com/k1LoW/donegroup#Cancel)

If cancel() and [donegroup.Wait](https://pkg.go.dev/github.com/k1LoW/donegroup#Wait) are to be executed at the same time, [donegroup.Cancel](https://pkg.go.dev/github.com/k1LoW/donegroup#Cancel) can be used.
[donegroup.Cancel](https://pkg.go.dev/github.com/k1LoW/donegroup#Cancel) can cancel the context.

``` go
ctx, cancel := donegroup.WithCancel(context.Background())
defer func() {
cancel()
if err := donegroup.Wait(ctx); err != nil {
log.Fatal(err)
}
}()
```

and
Can be cancelled anywhere with the context.

``` go
ctx, _ := donegroup.WithCancel(context.Background())

defer func() {
if err := donegroup.Cancel(ctx); err != nil {
log.Fatal(err)
}
if err := donegroup.Wait(ctx); err != nil {
log.Fatal(err)
}
}()
```

are equivalent.
56 changes: 6 additions & 50 deletions donegroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,27 +135,7 @@ func Cancel(ctx context.Context) error {

// CancelWithCause cancels the context with cause. Then calls the function registered by Cleanup.
func CancelWithCause(ctx context.Context, cause error) error {
return CancelWithKeyAndCause(ctx, doneGroupKey, cause)
}

// CancelWithTimeout cancels the context. Then calls the function registered by Cleanup with timeout.
func CancelWithTimeout(ctx context.Context, timeout time.Duration) error {
return CancelWithTimeoutAndKey(ctx, timeout, doneGroupKey)
}

// CancelWithTimeoutAndCause cancels the context with cause. Then calls the function registered by Cleanup with timeout.
func CancelWithTimeoutAndCause(ctx context.Context, timeout time.Duration, cause error) error {
return CancelWithTimeoutAndKeyAndCause(ctx, timeout, doneGroupKey, cause)
}

// CancelWithContext cancels the context. Then calls the function registered by Cleanup with context (ctxw).
func CancelWithContext(ctx, ctxw context.Context) error {
return CancelWithContextAndKey(ctx, ctxw, doneGroupKey)
}

// CancelWithCauseAndContext cancels the context with cause. Then calls the function registered by Cleanup with context (ctxw).
func CancelWithCauseAndContext(ctx context.Context, cause error, ctxw context.Context) error {
return CancelWithContextAndKeyAndCause(ctx, ctxw, doneGroupKey, cause)
return CancelWithCauseAndKey(ctx, cause, doneGroupKey)
}

// WaitWithKey blocks until the context is canceled. Then calls the function registered by Cleanup.
Expand Down Expand Up @@ -202,43 +182,19 @@ func WaitWithContextAndKey(ctx, ctxw context.Context, key any) error {
return dg.errors
}

// CancelWithKey cancels the context. Then calls the function registered by Cleanup.
// CancelWithKey cancels the context.
func CancelWithKey(ctx context.Context, key any) error {
return CancelWithContextAndKey(ctx, context.WithoutCancel(ctx), key)
}

// CancelWithKeyAndCause cancels the context with cause. Then calls the function registered by Cleanup.
func CancelWithKeyAndCause(ctx context.Context, key any, cause error) error {
return CancelWithContextAndKeyAndCause(ctx, context.WithoutCancel(ctx), key, cause)
}

// CancelWithTimeoutAndKey cancels the context. Then calls the function registered by Cleanup with timeout.
func CancelWithTimeoutAndKey(ctx context.Context, timeout time.Duration, key any) error {
ctxw, cancel := context.WithTimeout(context.WithoutCancel(ctx), timeout)
defer cancel()
return CancelWithContextAndKey(ctx, ctxw, key)
return CancelWithCauseAndKey(ctx, nil, key)
}

// CancelWithTimeoutAndKeyAndCause cancels the context with cause. Then calls the function registered by Cleanup with timeout.
func CancelWithTimeoutAndKeyAndCause(ctx context.Context, timeout time.Duration, key any, cause error) error {
ctxw, cancel := context.WithTimeout(context.WithoutCancel(ctx), timeout)
defer cancel()
return CancelWithContextAndKeyAndCause(ctx, ctxw, key, cause)
}

// CancelWithContextAndKey cancels the context. Then calls the function registered by Cleanup with context (ctxw).
func CancelWithContextAndKey(ctx, ctxw context.Context, key any) error {
return CancelWithContextAndKeyAndCause(ctx, ctxw, key, context.Canceled)
}

// CancelWithContextAndKeyAndCause cancels the context with cause. Then calls the function registered by Cleanup with context (ctxw).
func CancelWithContextAndKeyAndCause(ctx, ctxw context.Context, key any, cause error) error {
// CancelWithCauseAndKey cancels the context with cause.
func CancelWithCauseAndKey(ctx context.Context, cause error, key any) error {
dg, ok := ctx.Value(key).(*doneGroup)
if !ok {
return ErrNotContainDoneGroup
}
dg.cancel(cause)
return WaitWithContextAndKey(ctx, ctxw, key)
return nil
}

// Awaiter returns a function that guarantees execution of the process until it is called.
Expand Down
64 changes: 7 additions & 57 deletions donegroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,9 @@ func TestCancel(t *testing.T) {
if err != nil {
t.Error(err)
}
if !errors.Is(ctx.Err(), context.Canceled) {
t.Error("expected context.Canceled")
}
})

t.Run("Cancel without WithCancel", func(t *testing.T) {
Expand All @@ -523,54 +526,6 @@ func TestCancel(t *testing.T) {
})
}

func TestCancelWithTimeout(t *testing.T) {
t.Parallel()
ctx, _ := WithCancel(context.Background())

if err := Cleanup(ctx, func() error {
for i := 0; i < 10; i++ {
time.Sleep(2 * time.Millisecond)
}
return nil
}); err != nil {
t.Error(err)
}

timeout := 5 * time.Millisecond

defer func() {
time.Sleep(10 * time.Millisecond)
if err := CancelWithTimeout(ctx, timeout); !errors.Is(err, context.DeadlineExceeded) {
t.Error("expected timeout error")
}
}()
}

func TestCancelWithContext(t *testing.T) {
t.Parallel()
ctx, _ := WithCancel(context.Background())

if err := Cleanup(ctx, func() error {
for i := 0; i < 10; i++ {
time.Sleep(2 * time.Millisecond)
}
return nil
}); err != nil {
t.Error(err)
}

timeout := 5 * time.Millisecond

defer func() {
ctxx, cancelx := context.WithTimeout(context.Background(), timeout)
defer cancelx()
time.Sleep(10 * time.Millisecond)
if err := CancelWithContext(ctx, ctxx); !errors.Is(err, context.DeadlineExceeded) {
t.Error("expected timeout error")
}
}()
}

func TestGo(t *testing.T) {
t.Parallel()
tests := []struct {
Expand Down Expand Up @@ -773,8 +728,7 @@ func TestCancelWithCause(t *testing.T) {
t.Parallel()
var errTest = errors.New("test error")
t.Run("Timeout", func(t *testing.T) {
ctx, cancel := WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
ctx, _ := WithTimeout(context.Background(), 1*time.Millisecond)

if err := Wait(ctx); err != nil {
t.Error(err)
Expand All @@ -785,14 +739,10 @@ func TestCancelWithCause(t *testing.T) {
}
})

t.Run("Cancel with cause immediately", func(t *testing.T) {
ctx, cancel := WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
t.Run("Cancel with cause", func(t *testing.T) {
ctx, _ := WithCancel(context.Background())

if err := CancelWithTimeoutAndCause(ctx, 1*time.Millisecond, errTest); err != nil {
t.Error(err)
}
if err := Wait(ctx); err != nil {
if err := CancelWithCause(ctx, errTest); err != nil {
t.Error(err)
}

Expand Down

0 comments on commit c4778f1

Please sign in to comment.