I have this function:
func New(config *WatcherConfig) *Watcher {
instance := &Watcher{
config: config,
connections: make(map[string]ConnectionInfo),
lock: sync.Mutex{},
}
go instance.Start()
return instance
}
It creates a new instance of Watcher
, currently there are two WatcherConfig
. As you can see I start another function called Start()
using a Go routine.
func (w *Watcher) Start() {
fmt.Println(w.config.PathToLogFile)
}
One WatcherConfig
has a value of /var/log/openvpn.log
for PathToLogFile
and another WatcherConfig
has a value of /var/log/postfix.log
for PathToLogFile
. However when I call the Start()
function using a GoRoutine, it prints /var/log/postfix.log
twice. If I remove the go routine, so like this:
func New(config *WatcherConfig) *Watcher {
instance := &Watcher{
config: config,
connections: make(map[string]ConnectionInfo),
lock: sync.Mutex{},
}
instance.Start()
return instance
}
It will now print /var/log/openvpn.log
and /var/log/postfix.log
correctly.
Code that calls New()
/// ---------
/// Parse config file
/// ---------
configuration, err := config.ParseConfig(*configFileLocation)
if err != nil {
log.Fatalln(err)
}
var watchers []*watcher.Watcher
for _, watcherConfig := range configuration.Watchers {
watchers = append(watchers, watcher.New(&watcherConfig))
}
Why does the go routine "switch" to another instance?
CodePudding user response:
How to fix:
for _, watcherConfig := range configuration.Watchers {
watcherConfig := watcherConfig // copy
watchers = append(watchers, watcher.New(&watcherConfig))
}
Range loops in Go are re-using the iteration variable, i.e. they are re-using the allocated memory to reduce allocations, in your case it's the watcherConfig
variable. So the pointer you are passing to New
with New(&watcherConfig)
will point to the same memory (since it is re-used). Hence all watchers end up with the same config at the end.
The version without a goroutine in New
only looks to be working correctly because the print statement outputs the value currently stored in the shared memory, but once the loop ends, the watchers will, nevertheless, have a reference to the same, the last, config.
There's this FAQ entry that discusses the problem.