FAQ
Hello,

Say I have a blocking operation, and I want to `select` on it with some
other channel. The obvious thing to do, I thought, was to wrap the blocking
operation with a channel wrapper. i.e. a function that starts up a go
routine that would call the blocking operation and then return a channel
that will eventually be passed the result.

Now this worked, however, it's leaking goroutines. If the select statement
went with some other choice, then the block operation may potentially run
for ever with no way of closing.
Here is an example: (http://repl.it/90o)

package main

import "fmt"
import "time"

// A sample blocking operation, will loop and print.
func blockingFn() string {
     for i := 0; i < 10; i++ {
         time.Sleep(200 * time.Millisecond);
         fmt.Println("blocking operation running")
     }
     return "blocking operation done"
}

// Wrap the blocking operation with a channel and a goroutine
func nonBlockingWrapper() chan string {
     ch := make(chan string)
     go func() {
         ch <-blockingFn()
     }()
     return ch
}

func main() {
     select {
         case <- time.After(100 * time.Millisecond):
             fmt.Println("timeout")
         case res := <- nonBlockingWrapper():
             fmt.Println(res)
     }
     // Timer to illustrate that the blocking operation
     // from the prev select statement still runs. (see output)
     time.Sleep(1 * time.Second)
}

What's the best way to handle this?

--
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+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Search Discussions

  • Jesse McNelis at Jan 30, 2015 at 1:47 pm

    On Fri, Jan 30, 2015 at 8:13 PM, wrote:
    What's the best way to handle this?
    ch := make(chan string, 1)

    Put a buffer on the channel, when the operation completes it will fill
    the buffer which will just get garbage collected if your timeout has
    already triggered.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Amjad Masad at Jan 30, 2015 at 3:50 pm
    That doesn't help. See http://repl.it/90o/1 the goroutine still runs.
    What I want is to cancel the operation, cancel the goroutine once timeout
    has elapsed.
    On Friday, January 30, 2015 at 8:48:19 AM UTC-5, Jesse McNelis wrote:

    On Fri, Jan 30, 2015 at 8:13 PM, <amjad...@gmail.com <javascript:>>
    wrote:
    What's the best way to handle this?
    ch := make(chan string, 1)

    Put a buffer on the channel, when the operation completes it will fill
    the buffer which will just get garbage collected if your timeout has
    already triggered.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Silviunotathome at Jan 30, 2015 at 4:39 pm
    I think you need to simply do a for loop inside the main, and wait till
    it's done - but you need to call the wrapper function at the top, outside
    the loop

    *func main() {*

    * nonBlockingWrapperChannelResult := nonBlockingWrapper()*

    *Loop:*
    * for {*
    * select {*
    * case <-time.After(100 * time.Millisecond):*
    * fmt.Println("timeout")*
    * case res := <-nonBlockingWrapperChannelResult:*
    * fmt.Println("done wrapper: ", res)*
    * break Loop*
    * }*
    * }*
    * // Timer to illustrate that the blocking operation*
    * // from the prev select statement still runs.*
    * fmt.Println("sleeping one sec")*
    * time.Sleep(3 * time.Second)*
    * fmt.Println("really done")*
    *}*
    On Friday, January 30, 2015 at 10:50:43 AM UTC-5, Amjad Masad wrote:

    That doesn't help. See http://repl.it/90o/1 the goroutine still runs.
    What I want is to cancel the operation, cancel the goroutine once timeout
    has elapsed.
    On Friday, January 30, 2015 at 8:48:19 AM UTC-5, Jesse McNelis wrote:
    On Fri, Jan 30, 2015 at 8:13 PM, wrote:
    What's the best way to handle this?
    ch := make(chan string, 1)

    Put a buffer on the channel, when the operation completes it will fill
    the buffer which will just get garbage collected if your timeout has
    already triggered.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Amjad Masad at Jan 30, 2015 at 4:43 pm
    To clarify, this is an example for a real world use case. In the real
    world, I have a potentially infinitely blocking operation in a third party
    library that I call into with no way of canceling it.
    If it could potentially go forever then the loop won't help

    On Friday, January 30, 2015 at 11:39:28 AM UTC-5, silviun...@gmail.com
    wrote:
    I think you need to simply do a for loop inside the main, and wait till
    it's done - but you need to call the wrapper function at the top, outside
    the loop

    *func main() {*

    * nonBlockingWrapperChannelResult := nonBlockingWrapper()*

    *Loop:*
    * for {*
    * select {*
    * case <-time.After(100 * time.Millisecond):*
    * fmt.Println("timeout")*
    * case res := <-nonBlockingWrapperChannelResult:*
    * fmt.Println("done wrapper: ", res)*
    * break Loop*
    * }*
    * }*
    * // Timer to illustrate that the blocking operation*
    * // from the prev select statement still runs.*
    * fmt.Println("sleeping one sec")*
    * time.Sleep(3 * time.Second)*
    * fmt.Println("really done")*
    *}*
    On Friday, January 30, 2015 at 10:50:43 AM UTC-5, Amjad Masad wrote:

    That doesn't help. See http://repl.it/90o/1 the goroutine still runs.
    What I want is to cancel the operation, cancel the goroutine once timeout
    has elapsed.
    On Friday, January 30, 2015 at 8:48:19 AM UTC-5, Jesse McNelis wrote:
    On Fri, Jan 30, 2015 at 8:13 PM, wrote:
    What's the best way to handle this?
    ch := make(chan string, 1)

    Put a buffer on the channel, when the operation completes it will fill
    the buffer which will just get garbage collected if your timeout has
    already triggered.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Silviunotathome at Jan 30, 2015 at 5:38 pm
    Unfortunately, in this case you might have to do extra work. You will need
    to move the select and create a timer with an expiration channel (please
    see timer documentation) *inside* the non-blocking wrapper.
    You need to basically force the *nonBlockingWrapper() *to return,
    regardless of it being a legit value or a timer
    On Friday, January 30, 2015 at 11:43:42 AM UTC-5, Amjad Masad wrote:

    To clarify, this is an example for a real world use case. In the real
    world, I have a potentially infinitely blocking operation in a third party
    library that I call into with no way of canceling it.
    If it could potentially go forever then the loop won't help

    On Friday, January 30, 2015 at 11:39:28 AM UTC-5, silviun...@gmail.com
    wrote:
    I think you need to simply do a for loop inside the main, and wait till
    it's done - but you need to call the wrapper function at the top, outside
    the loop

    *func main() {*

    * nonBlockingWrapperChannelResult := nonBlockingWrapper()*

    *Loop:*
    * for {*
    * select {*
    * case <-time.After(100 * time.Millisecond):*
    * fmt.Println("timeout")*
    * case res := <-nonBlockingWrapperChannelResult:*
    * fmt.Println("done wrapper: ", res)*
    * break Loop*
    * }*
    * }*
    * // Timer to illustrate that the blocking operation*
    * // from the prev select statement still runs.*
    * fmt.Println("sleeping one sec")*
    * time.Sleep(3 * time.Second)*
    * fmt.Println("really done")*
    *}*
    On Friday, January 30, 2015 at 10:50:43 AM UTC-5, Amjad Masad wrote:

    That doesn't help. See http://repl.it/90o/1 the goroutine still runs.
    What I want is to cancel the operation, cancel the goroutine once
    timeout has elapsed.
    On Friday, January 30, 2015 at 8:48:19 AM UTC-5, Jesse McNelis wrote:
    On Fri, Jan 30, 2015 at 8:13 PM, wrote:
    What's the best way to handle this?
    ch := make(chan string, 1)

    Put a buffer on the channel, when the operation completes it will fill
    the buffer which will just get garbage collected if your timeout has
    already triggered.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Enormouspenguin at Jan 30, 2015 at 6:13 pm
    I don't think currently Goroutine has any mechanism to be preemptively
    killed like you want. And it's considered bad practice to do that because
    you could accidentally kill a Goroutine while it's holding a lock and then
    you know what... (deadlock)

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Amjad Masad at Jan 30, 2015 at 6:37 pm
    I'm coming to the same conclusion because also what happens to any
    resources that was opened by that call etc. I need to ask the library
    author (or send pull request) to have a close operation.

    Thanks
    On Friday, January 30, 2015, enormouspenguin wrote:

    I don't think currently Goroutine has any mechanism to be preemptively
    killed like you want. And it's considered bad practice to do that because
    you could accidentally kill a Goroutine while it's holding a lock and then
    you know what... (deadlock)
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedJan 30, '15 at 1:31p
activeJan 30, '15 at 6:37p
posts8
users4
websitegolang.org

People

Translate

site design / logo © 2022 Grokbase