mirror of
https://github.com/foxcpp/maddy.git
synced 2025-04-04 21:47:40 +03:00
Merge pull request #559 from domcyrus/fix-race-on-table-file
fix race condition on table.file #557
This commit is contained in:
commit
01ff37a9c5
2 changed files with 60 additions and 47 deletions
|
@ -124,24 +124,31 @@ func (f *File) reloader() {
|
|||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
f.reload()
|
||||
|
||||
case <-f.forceReload:
|
||||
f.reload()
|
||||
|
||||
case <-f.stopReloader:
|
||||
f.stopReloader <- struct{}{}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *File) reload() {
|
||||
info, err := os.Stat(f.file)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
f.mLck.Lock()
|
||||
f.m = map[string][]string{}
|
||||
f.mStamp = time.Now()
|
||||
f.mLck.Unlock()
|
||||
continue
|
||||
return
|
||||
}
|
||||
f.log.Error("os stat", err)
|
||||
}
|
||||
if info.ModTime().Before(f.mStamp) {
|
||||
continue // reload not necessary
|
||||
}
|
||||
case <-f.forceReload:
|
||||
case <-f.stopReloader:
|
||||
f.stopReloader <- struct{}{}
|
||||
return
|
||||
if info.ModTime().Before(f.mStamp) || time.Since(info.ModTime()) < (reloadInterval/2) {
|
||||
return // reload not necessary
|
||||
}
|
||||
|
||||
f.log.Debugf("reloading")
|
||||
|
@ -150,19 +157,29 @@ func (f *File) reloader() {
|
|||
if err := readFile(f.file, newm); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
f.log.Printf("ignoring non-existent file: %s", f.file)
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
f.log.Println(err)
|
||||
continue
|
||||
return
|
||||
}
|
||||
// after reading we need to check whether file has changed in between
|
||||
info2, err := os.Stat(f.file)
|
||||
if err != nil {
|
||||
f.log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !info2.ModTime().Equal(info.ModTime()) {
|
||||
// file has changed in the meantime
|
||||
return
|
||||
}
|
||||
|
||||
f.mLck.Lock()
|
||||
f.m = newm
|
||||
f.mStamp = time.Now()
|
||||
f.mStamp = info.ModTime()
|
||||
f.mLck.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (f *File) Close() error {
|
||||
f.stopReloader <- struct{}{}
|
||||
|
|
|
@ -111,27 +111,26 @@ func TestFileReload(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// This delay is somehow important. Not sure why.
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
// ensure it is correctly loaded at first time.
|
||||
m.mLck.RLock()
|
||||
if m.m["cat"] == nil {
|
||||
t.Fatalf("wrong content loaded, new m were not loaded, %v", m.m)
|
||||
}
|
||||
m.mLck.RUnlock()
|
||||
|
||||
if err := ioutil.WriteFile(f.Name(), []byte("dog: cat"), os.ModePerm); err != nil {
|
||||
for i := 0; i < 100; i++ {
|
||||
// try to provoke race condition on file writing
|
||||
if i%2 == 0 {
|
||||
if err := os.WriteFile(f.Name(), []byte("dog: cat"), os.ModePerm); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
time.Sleep(reloadInterval + 50*time.Millisecond)
|
||||
m.mLck.RLock()
|
||||
if m.m["dog"] != nil {
|
||||
m.mLck.RUnlock()
|
||||
break
|
||||
}
|
||||
m.mLck.RUnlock()
|
||||
}
|
||||
|
||||
time.Sleep(reloadInterval + 5*time.Millisecond)
|
||||
m.mLck.RLock()
|
||||
defer m.mLck.RUnlock()
|
||||
if m.m["dog"] == nil {
|
||||
t.Fatal("New m were not loaded")
|
||||
t.Fatalf("wrong content loaded, new m were not loaded, %v", m.m)
|
||||
}
|
||||
m.mLck.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,9 +207,6 @@ func TestFileReload_Removed(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// This delay is somehow important. Not sure why.
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
|
||||
os.Remove(f.Name())
|
||||
|
||||
time.Sleep(3 * reloadInterval)
|
||||
|
@ -223,5 +219,5 @@ func TestFileReload_Removed(t *testing.T) {
|
|||
}
|
||||
|
||||
func init() {
|
||||
reloadInterval = 250 * time.Millisecond
|
||||
reloadInterval = 10 * time.Millisecond
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue