-1

Assuming that I have a function that sends web requests to an API endpoint, I would like to add a timeout to the client so that if the call is taking too long, the operation breaks either by returning an error or panicing the current thread.

Another assumption is that, the client function (the function that sends web requests) comes from a library and it has been implemented in a synchronous way.

Let's have a look at the client function's signature:

func Send(params map[string]string) (*http.Response, error)

I would like to write a wrapper around this function to add a timeout mechanism. To do that, I can do:

func SendWithTimeout(ctx context.Context, params map[string]string) (*http.Response, error) {
    completed := make(chan bool)

    go func() {
        res, err := Send(params)
        _ = res
        _ = err
        completed <- true
    }()

    for {
        select {
        case <-ctx.Done():
            {
                return nil, errors.New("Cancelled")
            }
        case <-completed:
            {
                return nil, nil // just to test how this method works
            }
        }
    }
}

Now when I call the new function and pass a cancellable context, I successfully get a cancellation error, but the goroutine that is running the original Send function keeps on running to the end.

Since, the function makes an API call meaning that establishing socket/TCP connections are actually involved in the background, it is not a good practice to leave a long-running API behind the scene.

Is there any standard way to interrupt the original Send function when the context.Done() is hit?

4
  • Where are you passing the context ? Commented Aug 22, 2022 at 7:44
  • @Inian: What exactly do you mean? It's an argument of the function Commented Aug 22, 2022 at 7:45
  • 1
    If the context parameter is not passed to Send(), then what is the purpose of using it at all Commented Aug 22, 2022 at 7:48
  • @Inian: The whole purpose is to write a wrapper around the original Send that supports context Commented Aug 22, 2022 at 7:48

1 Answer 1

3

This is a "poor" design choice to add context support to an existing API / implementation that did not support it earlier. Context support should be added to the existing Send() implementation that uses it / monitors it, renaming it to SendWithTimeout(), and provide a new Send() function that takes no context, and calls SendWithTimeout() with context.TODO() or context.Background().

For example if your Send() function makes an outgoing HTTP call, that may be achieved by using http.NewRequest() followed by Client.Do(). In the new, context-aware version use http.NewRequestWithContext().

If you have a Send() function which you cannot change, then you're "out of luck". The function itself has to support the context or cancellation. You can't abort it from the outside.

See related:

Terminating function execution if a context is cancelled

Is it possible to cancel unfinished goroutines?

Stopping running function using context timeout in Golang

cancel a blocking operation in Go

Sign up to request clarification or add additional context in comments.

8 Comments

Yes, what you described is correct from a design perspective. My point is how to deal with something that already exists and cannot be modified
@ArnoldZahrneinder If you can't changeSend(), you're out of luck. You can't abort it from the outside. Send() itself must support context or a cancellation.
Ok, is there any way to inject a segment of new code into an existing function?
@ArnoldZahrneinder There is not.
Ok, thank you for making it clear.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.