plugin/reload: fix data races (#2567)
Reload didn't take proper care to protect the fields from use in different goroutines. Add a mutex and add helpers for usage and interval. Signed-off-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
parent
362d7e96dc
commit
a72f3a161c
2 changed files with 40 additions and 13 deletions
|
@ -2,6 +2,7 @@ package reload
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
|
@ -15,9 +16,34 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type reload struct {
|
type reload struct {
|
||||||
interval time.Duration
|
dur time.Duration
|
||||||
usage int
|
u int
|
||||||
quit chan bool
|
mtx sync.RWMutex
|
||||||
|
quit chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reload) setUsage(u int) {
|
||||||
|
r.mtx.Lock()
|
||||||
|
defer r.mtx.Unlock()
|
||||||
|
r.u = u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reload) usage() int {
|
||||||
|
r.mtx.RLock()
|
||||||
|
defer r.mtx.RUnlock()
|
||||||
|
return r.u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reload) setInterval(i time.Duration) {
|
||||||
|
r.mtx.Lock()
|
||||||
|
defer r.mtx.Unlock()
|
||||||
|
r.dur = i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reload) interval() time.Duration {
|
||||||
|
r.mtx.RLock()
|
||||||
|
defer r.mtx.RUnlock()
|
||||||
|
return r.dur
|
||||||
}
|
}
|
||||||
|
|
||||||
func hook(event caddy.EventName, info interface{}) error {
|
func hook(event caddy.EventName, info interface{}) error {
|
||||||
|
@ -28,7 +54,7 @@ func hook(event caddy.EventName, info interface{}) error {
|
||||||
// if reload is removed from the Corefile, then the hook
|
// if reload is removed from the Corefile, then the hook
|
||||||
// is still registered but setup is never called again
|
// is still registered but setup is never called again
|
||||||
// so we need a flag to tell us not to reload
|
// so we need a flag to tell us not to reload
|
||||||
if r.usage == unused {
|
if r.usage() == unused {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +64,7 @@ func hook(event caddy.EventName, info interface{}) error {
|
||||||
log.Infof("Running configuration MD5 = %x\n", md5sum)
|
log.Infof("Running configuration MD5 = %x\n", md5sum)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
tick := time.NewTicker(r.interval)
|
tick := time.NewTicker(r.interval())
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -53,15 +79,15 @@ func hook(event caddy.EventName, info interface{}) error {
|
||||||
md5sum = s
|
md5sum = s
|
||||||
// now lets consider that plugin will not be reload, unless appear in next config file
|
// now lets consider that plugin will not be reload, unless appear in next config file
|
||||||
// change status iof usage will be reset in setup if the plugin appears in config file
|
// change status iof usage will be reset in setup if the plugin appears in config file
|
||||||
r.usage = maybeUsed
|
r.setUsage(maybeUsed)
|
||||||
_, err := instance.Restart(corefile)
|
_, err := instance.Restart(corefile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Corefile changed but reload failed: %s", err)
|
log.Errorf("Corefile changed but reload failed: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// we are done, if the plugin was not set used, then it is not.
|
// we are done, if the plugin was not set used, then it is not.
|
||||||
if r.usage == maybeUsed {
|
if r.usage() == maybeUsed {
|
||||||
r.usage = unused
|
r.setUsage(unused)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,10 @@ func init() {
|
||||||
// it is used to transmit data between Setup and start of the hook called 'onInstanceStartup'
|
// it is used to transmit data between Setup and start of the hook called 'onInstanceStartup'
|
||||||
// channel for QUIT is never changed in purpose.
|
// channel for QUIT is never changed in purpose.
|
||||||
// WARNING: this data may be unsync after an invalid attempt of reload Corefile.
|
// WARNING: this data may be unsync after an invalid attempt of reload Corefile.
|
||||||
var r = reload{interval: defaultInterval, usage: unused, quit: make(chan bool)}
|
var (
|
||||||
var once sync.Once
|
r = reload{dur: defaultInterval, u: unused, quit: make(chan bool)}
|
||||||
var shutOnce sync.Once
|
once, shutOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
func setup(c *caddy.Controller) error {
|
func setup(c *caddy.Controller) error {
|
||||||
c.Next() // 'reload'
|
c.Next() // 'reload'
|
||||||
|
@ -69,8 +70,8 @@ func setup(c *caddy.Controller) error {
|
||||||
i = i + jitter
|
i = i + jitter
|
||||||
|
|
||||||
// prepare info for next onInstanceStartup event
|
// prepare info for next onInstanceStartup event
|
||||||
r.interval = i
|
r.setInterval(i)
|
||||||
r.usage = used
|
r.setUsage(used)
|
||||||
|
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
caddy.RegisterEventHook("reload", hook)
|
caddy.RegisterEventHook("reload", hook)
|
||||||
|
|
Loading…
Add table
Reference in a new issue