Depends on how quickly you want the expensive operation to terminate. Unless it 
is cpu bound the expensive operation probably is making IO calls - pass the 
context to those so the cancel will propagate. If it doesn’t I think a periodic 
poll/check of the context is simpler. 

> On Jan 8, 2021, at 9:17 PM, Amit Saha <amitsaha...@gmail.com> wrote:
> 
> 
> 
>> On 8 Jan 2021, at 1:59 pm, Robert Engels <reng...@ix.netcom.com> wrote:
>> 
>> You need to pass the context to the expensive work a periodically check if 
>> it has been cancelled. 
> 
> Thanks. I was thinking how to implement this. Is this a nice way to do it?
> 
> func clientDisconnected(ctx context.Context, done chan bool) bool {
>    select {
>    case <-done:
>        return false
>    case <-ctx.Done():
>        log.Printf("api: client disconnected.")
>        return true
>    }
> }
> 
> 
> func apiHandlerFunction(w http.ResponseWriter, r *http.Request) {
>    done := make(chan bool)
>    go func() {
>        log.Println("First expensive operation")
>        time.Sleep(5 * time.Second)
>        done <- true
>    }()
> 
>    if clientDisconnected(r.Context(), done) {
>        return
>    }
> 
>    go func() {
>        log.Println("Second expensive operation")
>        time.Sleep(5 * time.Second)
>        done <- true
>    }()
> 
>    if clientDisconnected(r.Context(), done) {
>        return
>    }
> 
>    fmt.Fprintf(w, "All operations done")
> }
> 
>> 
>>>> On Jan 7, 2021, at 8:55 PM, Amit Saha <amitsaha...@gmail.com> wrote:
>>> 
>>> Hi all, I want to confirm whether my understanding related to handling
>>> HTTP client disconnections is correct or not.
>>> 
>>> Consider this:
>>> 
>>> func longRunningProcessing(ctx context.Context, w http.ResponseWriter) {
>>>  done := make(chan bool)
>>>  go func() {
>>>      expensiveWork() # This is a blocking expensive processing call
>>>      done <- true
>>> }()
>>>  select {
>>>  case <-ctx.Done():
>>>    log.Printf("Client disconnected. Cancelling expensive operation.")
>>>    return
>>>   case <- done:
>>>    fmt.Fprintf(w, "Done!")
>>>  }
>>> }
>>> 
>>> func apiHandler(w http.ResponseWriter, r *http.Request) {
>>>  ctx := r.Context()
>>>  longRunningProcessing(ctx)
>>> }
>>> 
>>> When a client connection close is detected, the Done() call in select
>>> unblocks and the call goes out of the longRunningProcessing()
>>> function.
>>> However, the goroutine with the expensiveWork() is actually still running?
>>> 
>>> It seems like there is *no* way there is *not* going to be a goroutine
>>> leak (and resources associated with it) in a pattern like the above
>>> unless
>>> I have control over expensiveWork() and can instrument it with a child
>>> context. I am also thinking if there is a way to obtain the behavior
>>> similar to exec.CommandContext() command
>>> which does a hard kill of the external process when the context is 
>>> cancelled.
>>> 
>>> Thanks for any insights and clarifications.
>>> 
>>> -- 
>>> You received this message because you are subscribed to the Google Groups 
>>> "golang-nuts" group.
>>> To unsubscribe from this group and stop receiving emails from it, send an 
>>> email to golang-nuts+unsubscr...@googlegroups.com.
>>> To view this discussion on the web visit 
>>> https://groups.google.com/d/msgid/golang-nuts/CANODV3k9gF0gLWB1Z1OGaH-ArPbq8mNPKx9mzrTOTCa-kZZmvg%40mail.gmail.com.
>> 
> 

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/1F6B0741-5598-4705-B017-007D85A3719C%40ix.netcom.com.

Reply via email to