Behavior of time.Timer - go

I'm new to Go and the time package is a tad confusing for me. So I'm making a chat box and the idea is that when you send a message, the timer resets to 20 seconds, and if you don't respond by 20 seconds you get kicked out. The code I have works only if the person has typed something, but if they never input anything, they don't get kicked out. I tried applying Stop() before the for loop, but it doesn't work. Should I do all the timer before and after the loop rather than inside the for loop?
func ... {
timer := time.NewTimer(20 * time.Second)
for input.Scan() {
go func(){
<-timer.C
leaving <- ch
conn.Close()
}()
messages <- input.Text()
timer.Stop()
timer.Reset(20 * time.Second)
}

You can use time.AfterFunc; for example;
package main
import (
"fmt"
"time"
)
func main() {
timer := time.AfterFunc(time.Second*60, func() {
fmt.Printf("you're out!")
})
defer timer.Stop()
}
'cause whether user types something or not you can close the connection and send to leaving channel.

Related

Golang Channels - Help me get my head around this

Trying to get 2 Goroutines to play nicely within my project. Even though this is all sorta working, I am 100% sure it is being done very poorly... maybe even wrong...ly (is that even a word?).
Anyway, the basic concept of this project is to run some repeated work over a pre-selected amount of time while allowing the ability to abort the run before the time is up.
Here's the mess I have so far (I've only included the important parts for everyone's sanity... and to hide my horrible coding):
Two Goroutines:
BenchTimer() is a simple run-time countdown timer that does some repeating work for a set amount of time.
AbortTest() is a keyboard listener used to catch an 'ESC' (or whatever else I want) keypress from the keyboard to act as a "User Abort".
Each Goroutine, upon a successful run (i.e. BenchTimer() completes the countdown OR AbortTest() catches an abort keypress), sends a message down a common channel testAction. I use the one channel since this is an OR kinda thing (i.e. You can't get a completed countdown and an abort at the same time.). If BenchTimer() completes, then it sends "Complete" down the channel. If AbortTest() "completes" it sends "Abort" down the channel. [So far this all seems to be working...]
The next problem I ran into with this setup is how to kill the Goroutine that wasn't the "winner"... (i.e. If BenchTimer() completes normally, then I need to somehow kill AbortTest()... and vice-versa.) After a bunch of searching, I found that it isn't possible to kill a Goroutine externally, but it can be done internally... so I came up with using a second channel for each Goroutine to act as a sort of "kill signal" line: killAbortTest and killBenchTimer.
To tie this all together, I evaluate the result of the testAction channel. Because this channel will tell me which Goroutine "won", I can use this knowledge to send the correct (i.e. opposite) "kill signal" to have the "loser" Goroutine self-terminate.
Note: ... just means other code exists, but was removed due to not being needed for this post.
func main() {
...
testAction := make(chan string) // Action Result (Timer "Complete" or User "Abort")
killAbortTest := make(chan bool) // Kill AbortTest() Goroutine when BenchTimer() completes.
killBenchTimer := make(chan bool) // Kill BenchTimer() Goroutine when AbortTest() completes.
go BenchTimer(testAction, killBenchTimer) // Run BenchTimer() as Goroutine
go AbortTest(testAction, killAbortTest) // Run AbortTest() as Goroutine
// Program should wait here until it receives something on testAction channel.
actionVal := <-testAction
// Evaluate the testAction to kill the "loser" Goroutine
switch actionVal {
case "Abort":
killBenchTimer <- true // Abort received, signal BenchTimer() Goroutine to Quit
fmt.Println()
fmt.Println("Test Aborted")
case "Complete":
killAbortTest <- true // Countdown finished, signal AbortTest() Goroutine to Quit
fmt.Println()
fmt.Println("Test Completed")
}
...
}
// AbortTest - Listen for User Abort
func AbortTest(c chan<- string, k <-chan bool) {
if err := keyboard.Open(); err != nil {
panic(err)
}
defer func() {
_ = keyboard.Close()
}()
for {
select {
case <-k:
return
default:
_, key, err := keyboard.GetKey() // Poll for keypress
if err != nil {
panic(err)
}
if key == keyboard.KeyEsc { // ESC key was pressed
c <- "Abort"
return
}
}
}
}
// BenchTimer - Countdown Timer for BenchTest
func BenchTimer(c chan<- string, k <-chan bool) {
seconds := 0
switch testTime {
case "2-minute (fast)":
seconds = 120
case "5-minute (short)":
seconds = 300
case "10-minute (long)":
seconds = 600
case "20-minute (slow)":
seconds = 1200
}
ticker := time.Tick(time.Second)
for i := seconds; i >= 0; i-- {
select {
case <-k: // Kill Signal Received
return
default:
<-ticker
...
}
}
c <- "Complete"
}
There it is. My mess. There are many like it, but this one is my own. Like I said, it sorta works now, but I'm looking to make it better.
Am I just overthinking this whole process and making it way more complex than it needs to be?
Any help would be great.
This is the simplest example I can think of. I left out the keyboard part, but its basically the idea behind your code :
As mentioned by mh-cbon, this is safe :
package main
import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
rand.Seed(time.Now().UnixNano())
ctx, cancel := context.WithCancel(context.Background())
wg.Add(2)
go DoSomeTask(ctx, cancel)
go CancelTask(ctx, cancel)
wg.Wait()
}
func DoSomeTask(ctx context.Context, cancel func()) {
defer wg.Done()
defer cancel() // force cancellation
for i := 1; i < 10; i++ {
select {
case <-ctx.Done():
fmt.Println("context cancelled")
return
case <-time.After(time.Second):
}
fmt.Println("Done something!", i)
}
}
func CancelTask(ctx context.Context, cancel func()) {
defer wg.Done()
defer cancel() // force cancellation
duration := time.Second * time.Duration((rand.Intn(20-1) + 1))
fmt.Println("Will cancel in ", duration, " seconds!")
select {
case <-ctx.Done():
fmt.Println("context cancelled")
case <-time.After(duration):
}
}

How to cancel fmt.Scanf after a certain timeout?

I have a very simple command line utility and at the end of it I'm waiting for a user to hit the enter key to end the program:
fmt.Scanf("\n") // wait for hitting the enter key to end the program
Now I want to change it and if the enter key hasn't been hit for some time, I want to cancel Scanf and do something else. Is it possible to cancel waiting for user's input?
You can simply create a channel and launch a goroutine which does the fmt.Scanf("\n") and then writes something to the channel. Then select between that channel and time.After(3 * time.Second).
Here's a solution:
package main
import (
"fmt"
"time"
"os"
)
func main() {
// Rest of the program...
ch := make(chan int)
go func() {
fmt.Scanf("\n")
ch <- 1
}()
select {
case <-ch:
fmt.Println("Exiting.")
os.Exit(0)
case <-time.After(3 * time.Second):
fmt.Println("Timed out, exiting.")
}
}
NOTE: as pointed out by #Fabian and #YotKay in the comments, this will "leak" a goroutine (meaning it will remain running until the process exits) if the timeout expires and the user does not enter anything. This is still fine in a situation in which you have to either wait for input or exit the program, but not advisable in other cases, since it's not possible to "cancel" goroutines from the outside.
You just need to put it in an infinate loop
package main
import (
"fmt"
"time"
"context"
)
// 3 seconds for example
var deadline = time.Second * 3
func main() {
c := make(chan string, 1)
go scan(c)
ctx, _ := context.WithTimeout(context.Background(), deadline)
select {
case <-ctx.Done():
// didnt type for deadline seconds
case <-c:
// did it in time
}
}
func scan(in chan string) {
var input string
_, err := fmt.Scanln(&input)
if err != nil {
panic(err)
}
in <- input
}

Change a channel in select{case:channel}

I use a Ticker to execute tasks periodically, but I've got some problems when changing it. I'll change the ticker to a new one on receiving some messages and change the interval. Here's the sample code which will repro this problem:
package main
import (
"fmt"
"time"
)
type A struct {
ticker *time.Ticker
}
func (a *A) modify() {
a.ticker.Stop()
a.ticker = time.NewTicker(time.Second)
}
func main() {
a := new(A)
a.ticker = time.NewTicker(time.Second)
go func() {
for {
select {
case <-a.ticker.C:
fmt.Println("now")
go a.modify()
/*
default:
//fmt.Println("default")
time.Sleep(time.Millisecond * 100)
*/
}
}
}()
time.Sleep(time.Second * 60)
}
"now" will be printed only once. But it will be printed continously if I remove the "go", like this:
package main
import (
"fmt"
"time"
)
type A struct {
ticker *time.Ticker
}
func (a *A) modify() {
a.ticker.Stop()
a.ticker = time.NewTicker(time.Second)
}
func main() {
a := new(A)
a.ticker = time.NewTicker(time.Second)
go func() {
for {
select {
case <-a.ticker.C:
fmt.Println("now")
a.modify()
/*
default:
//fmt.Println("default")
time.Sleep(time.Millisecond * 100)
*/
}
}
}()
time.Sleep(time.Second * 60)
}
Also, if I leave the default clause un-commented, "now" can be printed continously.
Can anyone explain how would this happen?
The problem is that goroutine runs asynchronously.
your code behave like this when using a.modify():
get tick from a.ticker.C
stop old ticker, and create new a.ticker.C
wait for a.ticker.C using select
In this case, newly created a.ticker.C in 2. is identical to channel waiting in 3.
If you do 2. in goroutine. it may be done in following order
get tick from a.ticker.C
wait for a.ticker.C using select
stop old ticker, and create new a.ticker.C
In this case channel waiting in 2. is different from newly created channel in 3. .
Since selecting channel is old one which is stopped, it never gets any tick.
You can confirm this behavior inserting some fmt.Printf and watch for the address of a.ticker.C.
func (a *A) modify() {
a.ticker.Stop()
fmt.Printf("ticker stopped: %p\n", &a.ticker.C)
a.ticker = time.NewTicker(time.Second)
fmt.Printf("new ticker created: %p\n", &a.ticker.C)
}
func main() {
a := new(A)
a.ticker = time.NewTicker(time.Second)
go func() {
for {
fmt.Printf("waiting for ticker: %p\n", &a.ticker.C)
select {
....
with a.modify():
waiting for ticker: 0xc420010100
ticker stopped: 0xc420010100
new ticker created: 0xc420068000
waiting for ticker: 0xc420068000
ticker stopped: 0xc420068000
new ticker created: 0xc420068080
waiting for ticker: 0xc420068080
with go a.modify():
waiting for ticker: 0xc420010100
waiting for ticker: 0xc420010100
ticker stopped: 0xc420010100
new ticker created: 0xc420066040
you can see that with go a.modify() you are not waiting for a newly created channel.
UPDATE for behavior of default:
When using default: with go a.modify(), it will behave like this.
wait for a.ticker.C, got tick, call go a.modify() which does 3.
wait for a.ticker.C, got nothing, so fallback to default and sleep 100ms.
stop old ticker, update a.ticker.C
wait for a.ticker.C, got nothing, so fallback to default and sleep 100ms.
wait for a.ticker.C, got nothing, so fallback to default and sleep 100ms.
wait for a.ticker.C, got nothing, so fallback to default and sleep 100ms.
.....
wait for a.ticker.C, got tick, call go a.modify()
The point is that for{} loop can keep on going even when you got nothing from a.ticker.C.
You can confirm the behavior with same code.
waiting ticker: 0xc420010100 <-- 1.
now <-- 1.
waiting ticker: 0xc420010100 <-- 2.
default <-- 2.
ticker stopped: 0xc420010100 <-- 3.
new ticker created: 0xc420066240 <-- 3.
waiting ticker: 0xc420066240 <-- 4.
default <-- 4.

How to correctly use sync.Cond?

I'm having trouble figuring out how to correctly use sync.Cond. From what I can tell, a race condition exists between locking the Locker and invoking the condition's Wait method. This example adds an artificial delay between the two lines in the main goroutine to simulate the race condition:
package main
import (
"sync"
"time"
)
func main() {
m := sync.Mutex{}
c := sync.NewCond(&m)
go func() {
time.Sleep(1 * time.Second)
c.Broadcast()
}()
m.Lock()
time.Sleep(2 * time.Second)
c.Wait()
}
[Run on the Go Playground]
This causes an immediate panic:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Syncsemacquire(0x10330208, 0x1)
/usr/local/go/src/runtime/sema.go:241 +0x2e0
sync.(*Cond).Wait(0x10330200, 0x0)
/usr/local/go/src/sync/cond.go:63 +0xe0
main.main()
/tmp/sandbox301865429/main.go:17 +0x1a0
What am I doing wrong? How do I avoid this apparent race condition? Is there a better synchronization construct I should be using?
Edit: I realize I should have better explained the problem I'm trying to solve here. I have a long-running goroutine that downloads a large file and a number of other goroutines that need access to the HTTP headers when they are available. This problem is harder than it sounds.
I can't use channels since only one goroutine would then receive the value. And some of the other goroutines would be trying to retrieve the headers long after they are already available.
The downloader goroutine could simply store the HTTP headers in a variable and use a mutex to safeguard access to them. However, this doesn't provide a way for the other goroutines to "wait" for them to become available.
I had thought that both a sync.Mutex and sync.Cond together could accomplish this goal but it appears that this is not possible.
OP answered his own, but did not directly answer the original question, I am going to post how to correctly use sync.Cond.
You do not really need sync.Cond if you have one goroutine for each write and read - a single sync.Mutex would suffice to communicate between them. sync.Cond could useful in situations where multiple readers wait for the shared resources to be available.
var sharedRsc = make(map[string]interface{})
func main() {
var wg sync.WaitGroup
wg.Add(2)
m := sync.Mutex{}
c := sync.NewCond(&m)
go func() {
// this go routine wait for changes to the sharedRsc
c.L.Lock()
for len(sharedRsc) == 0 {
c.Wait()
}
fmt.Println(sharedRsc["rsc1"])
c.L.Unlock()
wg.Done()
}()
go func() {
// this go routine wait for changes to the sharedRsc
c.L.Lock()
for len(sharedRsc) == 0 {
c.Wait()
}
fmt.Println(sharedRsc["rsc2"])
c.L.Unlock()
wg.Done()
}()
// this one writes changes to sharedRsc
c.L.Lock()
sharedRsc["rsc1"] = "foo"
sharedRsc["rsc2"] = "bar"
c.Broadcast()
c.L.Unlock()
wg.Wait()
}
Playground
Having said that, using channels is still the recommended way to pass data around if the situation permitting.
Note: sync.WaitGroup here is only used to wait for the goroutines to complete their executions.
You need to make sure that c.Broadcast is called after your call to c.Wait. The correct version of your program would be:
package main
import (
"fmt"
"sync"
)
func main() {
m := &sync.Mutex{}
c := sync.NewCond(m)
m.Lock()
go func() {
m.Lock() // Wait for c.Wait()
c.Broadcast()
m.Unlock()
}()
c.Wait() // Unlocks m, waits, then locks m again
m.Unlock()
}
https://play.golang.org/p/O1r8v8yW6h
package main
import (
"fmt"
"sync"
"time"
)
func main() {
m := sync.Mutex{}
m.Lock() // main gouroutine is owner of lock
c := sync.NewCond(&m)
go func() {
m.Lock() // obtain a lock
defer m.Unlock()
fmt.Println("3. goroutine is owner of lock")
time.Sleep(2 * time.Second) // long computing - because you are the owner, you can change state variable(s)
c.Broadcast() // State has been changed, publish it to waiting goroutines
fmt.Println("4. goroutine will release lock soon (deffered Unlock")
}()
fmt.Println("1. main goroutine is owner of lock")
time.Sleep(1 * time.Second) // initialization
fmt.Println("2. main goroutine is still lockek")
c.Wait() // Wait temporarily release a mutex during wating and give opportunity to other goroutines to change the state.
// Because you don't know, whether this is state, that you are waiting for, is usually called in loop.
m.Unlock()
fmt.Println("Done")
}
http://play.golang.org/p/fBBwoL7_pm
Looks like you c.Wait for Broadcast which would never happens with your time intervals.
With
time.Sleep(3 * time.Second) //Broadcast after any Wait for it
c.Broadcast()
your snippet seems to work http://play.golang.org/p/OE8aP4i6gY .Or am I missing something that you try to achive?
I finally discovered a way to do this and it doesn't involve sync.Cond at all - just the mutex.
type Task struct {
m sync.Mutex
headers http.Header
}
func NewTask() *Task {
t := &Task{}
t.m.Lock()
go func() {
defer t.m.Unlock()
// ...do stuff...
}()
return t
}
func (t *Task) WaitFor() http.Header {
t.m.Lock()
defer t.m.Unlock()
return t.headers
}
How does this work?
The mutex is locked at the beginning of the task, ensuring that anything calling WaitFor() will block. Once the headers are available and the mutex unlocked by the goroutine, each call to WaitFor() will execute one at a time. All future calls (even after the goroutine ends) will have no problem locking the mutex, since it will always be left unlocked.
Yes you can use one channel to pass Header to multiple Go routines.
headerChan := make(chan http.Header)
go func() { // This routine can be started many times
header := <-headerChan // Wait for header
// Do things with the header
}()
// Feed the header to all waiting go routines
for more := true; more; {
select {
case headerChan <- r.Header:
default: more = false
}
}
This can be done with channels pretty easily and the code will be clean. Below is the example. Hope this helps!
package main
import (
"fmt"
"net/http"
"sync"
)
func main() {
done := make(chan struct{})
var wg sync.WaitGroup
// fork required number of goroutines
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
<-done
fmt.Println("read the http headers from here")
}()
}
time.Sleep(1) //download your large file here
fmt.Println("Unblocking goroutines...")
close(done) // this will unblock all the goroutines
wg.Wait()
}
In the excellent book "Concurrency in Go" they provide the following easy solution while leveraging the fact that a channel that is closed will release all waiting clients.
package main
import (
"fmt"
"time"
)
func main() {
httpHeaders := []string{}
headerChan := make(chan interface{})
var consumerFunc= func(id int, stream <-chan interface{}, funcHeaders *[]string)
{
<-stream
fmt.Println("Consumer ",id," got headers:", funcHeaders )
}
for i:=0;i<3;i++ {
go consumerFunc(i, headerChan, &httpHeaders)
}
fmt.Println("Getting headers...")
time.Sleep(2*time.Second)
httpHeaders=append(httpHeaders, "test1");
fmt.Println("Publishing headers...")
close(headerChan )
time.Sleep(5*time.Second)
}
https://play.golang.org/p/cE3SiKWNRIt

Interrupt a sleeping goroutine?

Is there a way in which I can execute, for example
time.Sleep(time.Second * 5000) //basically a long period of time
and then "wake up" the sleeping goroutine when I wish to do so?
I saw that there is a Reset(d Duration) in Sleep.go but I'm unable to invoke it.. Any thoughts?
There isn't a way to interrupt a time.Sleep, however, you can make use of time.After, and a select statement to get the functionality you're after.
Simple example to show the basic idea:
package main
import (
"fmt"
"time"
)
func main() {
timeoutchan := make(chan bool)
go func() {
<-time.After(2 * time.Second)
timeoutchan <- true
}()
select {
case <-timeoutchan:
break
case <-time.After(10 * time.Second):
break
}
fmt.Println("Hello, playground")
}
http://play.golang.org/p/7uKfItZbKG
In this example, we're spawning a signalling goroutine to tell main to stop pausing. The main is waiting and listening on two channels, timeoutchan (our signal) and the channel returned by time.After. When it receives on either of these channels, it will break out of the select and continue execution.

Resources