Skip to content

Commit c3409e7

Browse files
committed
write-lock-free
1 parent 4d3ebce commit c3409e7

File tree

1 file changed

+15
-8
lines changed

1 file changed

+15
-8
lines changed

modules/translation/i18n/i18n.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"os"
1111
"reflect"
1212
"sync"
13+
"sync/atomic"
1314
"time"
1415

1516
"code.gitea.io/gitea/modules/log"
@@ -31,7 +32,7 @@ type locale struct {
3132

3233
sourceFileName string
3334
sourceFileInfo os.FileInfo
34-
lastReloadCheckTime time.Time
35+
lastReloadCheckTime atomic.Value
3536
}
3637

3738
type LocaleStore struct {
@@ -66,6 +67,7 @@ func (ls *LocaleStore) AddLocaleByIni(langName, langDesc string, source interfac
6667
if fileName, ok := source.(string); ok {
6768
lc.sourceFileName = fileName
6869
lc.sourceFileInfo, _ = os.Stat(fileName) // live-reload only works for regular files. the error can be ignored
70+
lc.lastReloadCheckTime.Store(&time.Time{})
6971
}
7072

7173
ls.langNames = append(ls.langNames, langName)
@@ -146,22 +148,27 @@ func (l *locale) Tr(trKey string, trArgs ...interface{}) string {
146148
func (l *locale) tryTr(trKey string, trArgs ...interface{}) (msg string, found bool) {
147149
if l.store.reloadMu != nil {
148150
now := time.Now()
149-
if now.Sub(l.lastReloadCheckTime) >= time.Second && l.sourceFileInfo != nil && l.sourceFileName != "" {
151+
lastCheckTime, ok := l.lastReloadCheckTime.Load().(*time.Time)
152+
if ok && now.Sub(*lastCheckTime) >= time.Second && l.sourceFileInfo != nil {
153+
lastModTime := l.sourceFileInfo.ModTime()
154+
l.store.reloadMu.RUnlock() // the file may need to be reloaded with a write-lock, release the read-lock first
155+
150156
sourceFileInfo, err := os.Stat(l.sourceFileName)
151-
l.store.reloadMu.RUnlock() // if the locale file should be reloaded, then we release the read-lock
152-
l.store.reloadMu.Lock() // and acquire the write-lock
153-
l.lastReloadCheckTime = now
154-
if err == nil && !sourceFileInfo.ModTime().Equal(l.sourceFileInfo.ModTime()) {
157+
casOk := l.lastReloadCheckTime.CompareAndSwap(lastCheckTime, &now)
158+
if err == nil && casOk && !sourceFileInfo.ModTime().Equal(lastModTime) {
159+
l.store.reloadMu.Lock() // acquire the write-lock only if the file need to be reloaded
155160
if err = l.store.reloadLocaleByIni(l.langName, l.sourceFileName); err == nil {
156161
l.sourceFileInfo = sourceFileInfo
162+
log.Info("reloaded locale file %q", l.sourceFileName)
157163
} else {
158164
log.Error("unable to live-reload the locale file %q, err: %v", l.sourceFileName, err)
159165
}
166+
l.store.reloadMu.Unlock() // release the write-lock
160167
} else if err != nil {
161168
log.Error("unable to stat the locale file %q, err: %v", l.sourceFileName, err)
162169
}
163-
l.store.reloadMu.Unlock() // release the write-lock
164-
l.store.reloadMu.RLock() // and re-acquire the read-lock, which was managed by outer Tr function
170+
171+
l.store.reloadMu.RLock() // re-acquire the read-lock, which was managed by outer Tr function
165172
}
166173
}
167174
trMsg := trKey

0 commit comments

Comments
 (0)