[go-nuts] Why doesn't this select statement timeout?

2018-05-21 Thread gopher . nutter
The following setup uses a select statement to either wait for a response 
error or timeout after a given duration.

The httptest.Server is setup to force the timeout by sleeping (1 sec) for 
greater than the timeout (1 millisec).

But the timeout case isn't being hit at all, why? what is wrong with the 
setup?

https://play.golang.org/p/jX_nZPV4vVY

package main

import (
"fmt"
"time"
"net/http"
"net/http/httptest"
"errors"
)

func main() {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r 
*http.Request) {
time.Sleep(1 * time.Second)
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()

req, _ := http.NewRequest(http.MethodGet, ts.URL, nil)
f := func(r *http.Request) error {
_, err := http.DefaultClient.Do(r)
return err
}
ch := make(chan error, 1)
timeout := 1 * time.Millisecond
select {
case ch <-f(req):
case <-time.After(timeout):
ch <- errors.New("timeout")
}
err:= <-ch
close(ch)
fmt.Println(err) // outputs , not timeout
// Output:
// timeout
}


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


Re: [go-nuts] Why doesn't this select statement timeout?

2018-05-24 Thread gopher . nutter
Thanks for the reply.

So you're saying that all case statements are evaluated first before a 
choice is made.
Which means that the full request/response must complete even if the 
timeout case finishes first.

If I swap the cases, now I consistently get a `timeout`. 
All cases are run to completion, so the full request/response occurs.
Whats the reason for selecting the timeout? shouldn't it be pseudo-random?
https://play.golang.org/p/M-7rMPJxOWq

How should the done channel be setup to get this example working properly?



On Tuesday, May 22, 2018 at 5:29:43 PM UTC-7, alex@gmail.com wrote:
>
> https://golang.org/ref/spec#Select_statements
>
> Execution of a "select" statement proceeds in several steps:
>>
>>1. For all the cases in the statement, the channel operands of 
>>receive operations and the channel and right-hand-side expressions of 
>> send 
>>statements are evaluated exactly once, in source order, upon entering the 
>>"select" statement. The result is a set of channels to receive from or 
>> send 
>>to, and the corresponding values to send. Any side effects in that 
>>evaluation will occur irrespective of which (if any) communication 
>>operation is selected to proceed. Expressions on the left-hand side of a 
>>RecvStmt with a short variable declaration or assignment are not yet 
>>evaluated.
>>2. If one or more of the communications can proceed, a single one 
>>that can proceed is chosen via a uniform pseudo-random selection. 
>>Otherwise, if there is a default case, that case is chosen. If there is 
>> no 
>>default case, the "select" statement blocks until at least one of the 
>>communications can proceed.
>>3. Unless the selected case is the default case, the respective 
>>communication operation is executed.
>>4. If the selected case is a RecvStmt with a short variable 
>>declaration or an assignment, the left-hand side expressions are 
>> evaluated 
>>and the received value (or values) are assigned.
>>5. The statement list of the selected case is executed.
>>
>>
> So it evaluates all your function calls before doing the select in the 
> order they appear in single threadeddly. 
>
> On Wednesday, 23 May 2018 07:29:59 UTC+8, ag wrote:
>>
>> This is interesting and I still don't get why it's not working.
>>
>> I modified the code a bit and expected it to immediately break out of 
>> select but it still waits for http calls to finish
>>
>> https://play.golang.org/p/z1mmqpQtOre
>>
>> package main
>>
>> import (
>> "errors"
>> "fmt"
>> "net/http"
>> "net/http/httptest"
>> "time"
>> )
>>
>> func main() {
>> ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r 
>> *http.Request) {
>> time.Sleep(1 * time.Second)
>> w.WriteHeader(http.StatusOK)
>> }))
>> defer ts.Close()
>>
>> req, _ := http.NewRequest(http.MethodGet, ts.URL, nil)
>>
>> f := func(r *http.Request, n int) error {
>> fmt.Println("Making the http call ", n)
>> _, err := http.DefaultClient.Do(r)
>> return err
>> }
>>
>> ch := make(chan error, 1)
>>
>> timeout := 1 * time.Millisecond
>> select {
>> case ch <- errors.New("immediate timeout"):
>> case ch <- f(req, 1):
>> case ch <- f(req, 2):
>> case <-time.After(timeout):
>> fmt.Println("Timeout being called")
>> ch <- errors.New("timeout")
>> }
>>
>> err1 := <-ch
>> fmt.Println("Received first error", err1)
>> close(ch)
>>
>>
>> // Output:
>>
>>// Making the http call  1   // Making the http call  2   // 
>> Received first error immediate timeout
>>
>> }
>>
>>
>>
>>
>>
>> On Monday, May 21, 2018 at 10:25:08 PM UTC-7, Burak Serdar wrote:
>>>
>>> On Mon, May 21, 2018 at 12:13 PM,   wrote: 
>>> > timeout := 1 * time.Millisecond 
>>> > select { 
>>> > case ch <-f(req): 
>>> > case <-time.After(timeout): 
>>> > ch <- errors.New("timeout") 
>>> > } 
>>>
>>> The instant 'select' is evaluated, ch is writable. Also time.After() 
>>> is called, and returns a channel that will be ready to be read in a 
>>> millisecond, i.e. not readable immediately. So it'll write to ch, and 
>>> will never timeout. 
>>>
>>> To do what you described, you need to run the request piece in a 
>>> goroutine, and write the result to a channel: 
>>>
>>> go func() { 
>>>_,err:=http.DefaultClient.Do(...) 
>>>ch<-err 
>>> }() 
>>>
>>> Then, wait for the error to arrive on ch, or timeout: 
>>>
>>> select { 
>>> case err:=<-ch: 
>>> case <-time.After(timeout): 
>>> } 
>>>
>>>
>>> > err:= <-ch 
>>> > close(ch) 
>>> > fmt.Println(err) // outputs , not timeout 
>>> > // Output: 
>>> > // timeout 
>>> > } 
>>> > 
>>> > 
>>> > -- 
>>> > 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...@googlegroups.com. 
>>> > For more options, visit https://groups.google.com/d/optout. 
>>>
>>

-- 
You received this message because you are subscribed t

Re: [go-nuts] Why doesn't this select statement timeout?

2018-05-25 Thread gopher . nutter
That is an excellent example. Thanks!

On Thursday, May 24, 2018 at 8:26:36 AM UTC-7, alex@gmail.com wrote:
>
> No, not the entire case statements, only the statements on the right of 
> the "<-"
>
> btw if you run it out of the playground (which makes concurrency more 
> deterministic)
> You would get timeout sometimes and nil sometimes
>
> Also maybe this example would help you understand what's going on.
> https://play.golang.org/p/DokMlHtdzi7
>
> On Thursday, 24 May 2018 16:58:20 UTC+8, gopher...@gmail.com wrote:
>>
>> Thanks for the reply.
>>
>> So you're saying that all case statements are evaluated first before a 
>> choice is made.
>> Which means that the full request/response must complete even if the 
>> timeout case finishes first.
>>
>> If I swap the cases, now I consistently get a `timeout`. 
>> All cases are run to completion, so the full request/response occurs.
>> Whats the reason for selecting the timeout? shouldn't it be pseudo-random?
>> https://play.golang.org/p/M-7rMPJxOWq
>>
>> How should the done channel be setup to get this example working properly?
>>
>>
>>
>> On Tuesday, May 22, 2018 at 5:29:43 PM UTC-7, alex@gmail.com wrote:
>>>
>>> https://golang.org/ref/spec#Select_statements
>>>
>>> Execution of a "select" statement proceeds in several steps:

1. For all the cases in the statement, the channel operands of 
receive operations and the channel and right-hand-side expressions of 
 send 
statements are evaluated exactly once, in source order, upon entering 
 the 
"select" statement. The result is a set of channels to receive from or 
 send 
to, and the corresponding values to send. Any side effects in that 
evaluation will occur irrespective of which (if any) communication 
operation is selected to proceed. Expressions on the left-hand side of 
 a 
RecvStmt with a short variable declaration or assignment are not yet 
evaluated.
2. If one or more of the communications can proceed, a single one 
that can proceed is chosen via a uniform pseudo-random selection. 
Otherwise, if there is a default case, that case is chosen. If there is 
 no 
default case, the "select" statement blocks until at least one of the 
communications can proceed.
3. Unless the selected case is the default case, the respective 
communication operation is executed.
4. If the selected case is a RecvStmt with a short variable 
declaration or an assignment, the left-hand side expressions are 
 evaluated 
and the received value (or values) are assigned.
5. The statement list of the selected case is executed.


>>> So it evaluates all your function calls before doing the select in the 
>>> order they appear in single threadeddly. 
>>>
>>> On Wednesday, 23 May 2018 07:29:59 UTC+8, ag wrote:

 This is interesting and I still don't get why it's not working.

 I modified the code a bit and expected it to immediately break out of 
 select but it still waits for http calls to finish

 https://play.golang.org/p/z1mmqpQtOre

 package main

 import (
 "errors"
 "fmt"
 "net/http"
 "net/http/httptest"
 "time"
 )

 func main() {
 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r 
 *http.Request) {
 time.Sleep(1 * time.Second)
 w.WriteHeader(http.StatusOK)
 }))
 defer ts.Close()

 req, _ := http.NewRequest(http.MethodGet, ts.URL, nil)

 f := func(r *http.Request, n int) error {
 fmt.Println("Making the http call ", n)
 _, err := http.DefaultClient.Do(r)
 return err
 }

 ch := make(chan error, 1)

 timeout := 1 * time.Millisecond
 select {
 case ch <- errors.New("immediate timeout"):
 case ch <- f(req, 1):
 case ch <- f(req, 2):
 case <-time.After(timeout):
 fmt.Println("Timeout being called")
 ch <- errors.New("timeout")
 }

 err1 := <-ch
 fmt.Println("Received first error", err1)
 close(ch)


 // Output:

// Making the http call  1   // Making the http call  2   
 // Received first error immediate timeout

 }





 On Monday, May 21, 2018 at 10:25:08 PM UTC-7, Burak Serdar wrote:
>
> On Mon, May 21, 2018 at 12:13 PM,   wrote: 
> > timeout := 1 * time.Millisecond 
> > select { 
> > case ch <-f(req): 
> > case <-time.After(timeout): 
> > ch <- errors.New("timeout") 
> > } 
>
> The instant 'select' is evaluated, ch is writable. Also time.After() 
> is called, and returns a channel that will be ready to be read in a 
> millisecond, i.e. not readable immediately. So it'll write to ch, and 
> will never timeout. 
>
> To do what you described, you need to run the request piece in a 
> gorou