Skip to content

Commit 25d508e

Browse files
google-labs-jules[bot]jaeyeom
authored andcommitted
refactor(timeout): remove unreachable code in select statement
This commit removes an unreachable `if !ok` block from the select statement within the `DoWithTimeout` function in the `timeout` package. The `done` channel is buffered and the value is sent before the channel is closed by the sender goroutine. This guarantees that if the `case err := <-done:` is selected, the read will always be successful and `ok` (in a two-value receive) would have been true. Therefore, the `if !ok` check was dead code. The code was simplified to a single-value receive `case err := <-done:`. The unnecessary import of the "errors" package, which was only used by the dead code, has also been removed. Existing tests continue to pass, confirming the change is safe.
1 parent 6fd1744 commit 25d508e

2 files changed

Lines changed: 159 additions & 0 deletions

File tree

timeout/timeout.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Package timeout provides a utility function to execute a function with a
2+
// timeout. It helps in scenarios where an operation needs to be bound by a time
3+
// limit, preventing indefinite blocking.
4+
package timeout
5+
6+
import (
7+
"context"
8+
"time"
9+
)
10+
11+
// DoWithTimeout executes the given function f within the specified timeout
12+
// duration. It takes a parent context, a timeout duration, and the function to
13+
// execute. The function f is of type func() error.
14+
//
15+
// DoWithTimeout returns nil if f completes successfully within the timeout. If
16+
// f returns an error, DoWithTimeout returns that error. If the timeout duration
17+
// is reached before f completes, DoWithTimeout returns
18+
// [context.DeadlineExceeded]. If the parent context is canceled before f
19+
// completes, DoWithTimeout returns [context.Canceled].
20+
func DoWithTimeout(ctx context.Context, timeout time.Duration, f func() error) error {
21+
ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout)
22+
defer cancel()
23+
24+
// Buffer of 1 to prevent sender from blocking if receiver is not ready
25+
done := make(chan error, 1)
26+
27+
go func() {
28+
// Close the channel when the goroutine exits
29+
defer close(done)
30+
done <- f()
31+
}()
32+
33+
select {
34+
case <-ctxWithTimeout.Done():
35+
return ctxWithTimeout.Err()
36+
case err := <-done:
37+
return err
38+
}
39+
}

timeout/timeout_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package timeout
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"testing"
8+
"time"
9+
)
10+
11+
func TestDoWithTimeout_Success(t *testing.T) {
12+
ctx := context.Background()
13+
timeout := 100 * time.Millisecond
14+
f := func() error {
15+
time.Sleep(10 * time.Millisecond)
16+
return nil
17+
}
18+
19+
err := DoWithTimeout(ctx, timeout, f)
20+
if err != nil {
21+
t.Errorf("Expected no error, got %v", err)
22+
}
23+
}
24+
25+
func TestDoWithTimeout_Timeout(t *testing.T) {
26+
ctx := context.Background()
27+
timeout := 10 * time.Millisecond
28+
f := func() error {
29+
time.Sleep(100 * time.Millisecond)
30+
return nil
31+
}
32+
33+
err := DoWithTimeout(ctx, timeout, f)
34+
if err != context.DeadlineExceeded {
35+
t.Errorf("Expected context.DeadlineExceeded, got %v", err)
36+
}
37+
}
38+
39+
func TestDoWithTimeout_ContextCanceled(t *testing.T) {
40+
ctx, cancel := context.WithCancel(context.Background())
41+
timeout := 100 * time.Millisecond
42+
f := func() error {
43+
time.Sleep(10 * time.Millisecond)
44+
return nil
45+
}
46+
47+
// Cancel context before calling DoWithTimeout
48+
cancel()
49+
err := DoWithTimeout(ctx, timeout, f)
50+
if err != context.Canceled {
51+
t.Errorf("Expected context.Canceled, got %v", err)
52+
}
53+
}
54+
55+
func ExampleDoWithTimeout() {
56+
// Scenario 1: Function completes successfully within timeout
57+
ctx1 := context.Background()
58+
timeout1 := 100 * time.Millisecond
59+
f1 := func() error {
60+
time.Sleep(10 * time.Millisecond)
61+
fmt.Println("Function 1 completed")
62+
return nil
63+
}
64+
err1 := DoWithTimeout(ctx1, timeout1, f1)
65+
if err1 != nil {
66+
fmt.Printf("Function 1 error: %v\n", err1)
67+
}
68+
69+
// Scenario 2: Function times out
70+
ctx2 := context.Background()
71+
timeout2 := 10 * time.Millisecond
72+
f2 := func() error {
73+
time.Sleep(100 * time.Millisecond)
74+
fmt.Println("Function 2 completed (this should not print if timeout works)")
75+
return nil
76+
}
77+
err2 := DoWithTimeout(ctx2, timeout2, f2)
78+
if err2 != nil {
79+
fmt.Printf("Function 2 error: %v\n", err2)
80+
}
81+
82+
// Output:
83+
// Function 1 completed
84+
// Function 2 error: context deadline exceeded
85+
}
86+
87+
func TestDoWithTimeout_FunctionError(t *testing.T) {
88+
ctx := context.Background()
89+
timeout := 100 * time.Millisecond
90+
expectedErr := errors.New("function error")
91+
f := func() error {
92+
return expectedErr
93+
}
94+
95+
err := DoWithTimeout(ctx, timeout, f)
96+
if err != expectedErr {
97+
t.Errorf("Expected error %v, got %v", expectedErr, err)
98+
}
99+
}
100+
101+
func TestDoWithTimeout_ContextCanceledDuringExecution(t *testing.T) {
102+
ctx, cancel := context.WithCancel(context.Background())
103+
timeout := 100 * time.Millisecond
104+
f := func() error {
105+
// Sleep long enough for cancellation to occur
106+
time.Sleep(50 * time.Millisecond)
107+
return nil
108+
}
109+
110+
go func() {
111+
// Wait a bit then cancel
112+
time.Sleep(10 * time.Millisecond)
113+
cancel()
114+
}()
115+
116+
err := DoWithTimeout(ctx, timeout, f)
117+
if err != context.Canceled {
118+
t.Errorf("Expected context.Canceled, got %v", err)
119+
}
120+
}

0 commit comments

Comments
 (0)