I am learning Go by writing a simple program that concurrently downloads sensor data files from a few http servers. The sensor data files on the servers are refreshed at regular intervals (30 seconds or 2 minutes, depends on the 'origin'). Downloading the data can take from 100ms to 10 seconds. So I read some configurations for each server (OriginContext). Then I start a controller for each OriginContext. Each controller continuously fires a goroutine that does the download etc.
I stripped my code down to a minimal example that somehow/hopefully still shows my intentions. When I run it, there will be two controllers, but somehow when they fire the doStuffThatMayTakeLongTime() methods they all refer to the identical configuration.
So, how did I confuse scopes of variables and pointers in goroutines here?
I am very new to Go and also this is the first time I try to use a language that uses pointers. Well, my shy C/C++ attempts are more than a decade ago... so I assume my confusion is with reference/value/dereference, but I can't see it.
This is the code:
package main
import (
"log"
"time"
)
type OriginContext struct {
Origin string
Offset time.Duration
Interval time.Duration
}
type Controller struct {
originContext *OriginContext
}
func NewController(originContext *OriginContext) (w *Controller) {
log.Printf("Controller starting loop for origin %s.", originContext.Origin)
w = &Controller{originContext}
w.start()
return w
}
func (w *Controller) start() {
log.Println("start() of", w.originContext.Origin)
go func() {
time.Sleep(w.originContext.Offset)
ticker := time.NewTicker(w.originContext.Interval)
go w.doStuffThatMayTakeLongTime() // iteration zero
for {
select {
case <-ticker.C:
go w.doStuffThatMayTakeLongTime()
}
}
}()
}
func (w *Controller) doStuffThatMayTakeLongTime() {
log.Printf("%s doing stuff", w.originContext.Origin)
}
func main() {
contexts := []OriginContext{
{
Origin: "alpha",
Offset: 0 * time.Second,
Interval: 5 * time.Second,
},
{
Origin: "bravo",
Offset: 5 * time.Second,
Interval: 10 * time.Second,
},
}
for _, ctx := range contexts {
log.Printf("Starting Controller %s.", ctx.Origin)
_ = NewController(&ctx)
}
select {}
}
And this is some output:
2015/09/07 14:30:11 Starting Controller alpha.
2015/09/07 14:30:11 Controller starting loop for origin alpha.
2015/09/07 14:30:11 start() of alpha
2015/09/07 14:30:11 Starting Controller bravo.
2015/09/07 14:30:11 Controller starting loop for origin bravo.
2015/09/07 14:30:11 start() of bravo
2015/09/07 14:30:16 bravo doing stuff
2015/09/07 14:30:16 bravo doing stuff
2015/09/07 14:30:26 bravo doing stuff
2015/09/07 14:30:26 bravo doing stuff
There should be alpha and bravo doing stuff, but there is just bravo.