From 6f39028719195ee3e3e8c37ec4d24a1e62d19a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?= Date: Sat, 23 Aug 2025 16:05:36 +0000 Subject: [PATCH 1/5] refator timer to use sync.Once --- common/signal/timer.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/common/signal/timer.go b/common/signal/timer.go index ece9f496794c..a48ac7650780 100644 --- a/common/signal/timer.go +++ b/common/signal/timer.go @@ -18,6 +18,8 @@ type ActivityTimer struct { updated chan struct{} checkTask *task.Periodic onTimeout func() + consumed bool + once sync.Once } func (t *ActivityTimer) Update() { @@ -37,39 +39,40 @@ func (t *ActivityTimer) check() error { } func (t *ActivityTimer) finish() { - t.Lock() - defer t.Unlock() + t.once.Do(func() { + t.Lock() + defer t.Unlock() - if t.onTimeout != nil { t.onTimeout() - t.onTimeout = nil - } - if t.checkTask != nil { t.checkTask.Close() - t.checkTask = nil - } + t.consumed = true + }) } func (t *ActivityTimer) SetTimeout(timeout time.Duration) { + if t.consumed { + return + } if timeout == 0 { t.finish() return } - checkTask := &task.Periodic{ + newCheckTask := &task.Periodic{ Interval: timeout, Execute: t.check, } t.Lock() + defer t.Unlock() + // only in initial if t.checkTask != nil { t.checkTask.Close() } - t.checkTask = checkTask + t.checkTask = newCheckTask t.Update() - common.Must(checkTask.Start()) - t.Unlock() + common.Must(newCheckTask.Start()) } func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer { From ade3cf61e329e014860294002f3986723cef0a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?= Date: Sat, 23 Aug 2025 16:08:18 +0000 Subject: [PATCH 2/5] Unexport mutex --- common/signal/timer.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/signal/timer.go b/common/signal/timer.go index a48ac7650780..aa6894bc72d6 100644 --- a/common/signal/timer.go +++ b/common/signal/timer.go @@ -14,8 +14,8 @@ type ActivityUpdater interface { } type ActivityTimer struct { - sync.RWMutex - updated chan struct{} + mu sync.RWMutex + updated chan struct{} checkTask *task.Periodic onTimeout func() consumed bool @@ -40,8 +40,8 @@ func (t *ActivityTimer) check() error { func (t *ActivityTimer) finish() { t.once.Do(func() { - t.Lock() - defer t.Unlock() + t.mu.Lock() + defer t.mu.Unlock() t.onTimeout() t.checkTask.Close() @@ -63,8 +63,8 @@ func (t *ActivityTimer) SetTimeout(timeout time.Duration) { Execute: t.check, } - t.Lock() - defer t.Unlock() + t.mu.Lock() + defer t.mu.Unlock() // only in initial if t.checkTask != nil { From 7307390c3bb3f3ff28c347cc8c67bc83e7121ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?= Date: Sat, 23 Aug 2025 16:29:11 +0000 Subject: [PATCH 3/5] fix nil --- common/signal/timer.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/common/signal/timer.go b/common/signal/timer.go index aa6894bc72d6..69cbdcb9db1f 100644 --- a/common/signal/timer.go +++ b/common/signal/timer.go @@ -14,8 +14,8 @@ type ActivityUpdater interface { } type ActivityTimer struct { - mu sync.RWMutex - updated chan struct{} + mu sync.RWMutex + updated chan struct{} checkTask *task.Periodic onTimeout func() consumed bool @@ -43,6 +43,9 @@ func (t *ActivityTimer) finish() { t.mu.Lock() defer t.mu.Unlock() + if t.checkTask != nil { + t.checkTask.Close() + } t.onTimeout() t.checkTask.Close() t.consumed = true From 13851d905b46e1aa4762e31685537b8b37a5c313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?= Date: Sun, 24 Aug 2025 01:58:31 +0000 Subject: [PATCH 4/5] fix nil --- common/common.go | 13 +++++++++++++ common/signal/timer.go | 10 ++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/common/common.go b/common/common.go index c3bfa944f6d9..a726ec1f27b4 100644 --- a/common/common.go +++ b/common/common.go @@ -7,6 +7,7 @@ import ( "go/build" "os" "path/filepath" + "reflect" "strings" "github.com/xtls/xray-core/common/errors" @@ -153,3 +154,15 @@ func GetModuleName(pathToProjectRoot string) (string, error) { } return moduleName, fmt.Errorf("no `go.mod` file in every parent directory of `%s`", pathToProjectRoot) } + +// CloseIfExists call obj.Close() if obj is not nil. +func CloseIfExists(obj any) error { + if obj != nil { + v := reflect.ValueOf(obj) + if !v.IsNil() { + fmt.Println("closing", obj) + return Close(obj) + } + } + return nil +} diff --git a/common/signal/timer.go b/common/signal/timer.go index 69cbdcb9db1f..da10d621d4cb 100644 --- a/common/signal/timer.go +++ b/common/signal/timer.go @@ -43,11 +43,8 @@ func (t *ActivityTimer) finish() { t.mu.Lock() defer t.mu.Unlock() - if t.checkTask != nil { - t.checkTask.Close() - } + common.CloseIfExists(t.checkTask) t.onTimeout() - t.checkTask.Close() t.consumed = true }) } @@ -69,10 +66,7 @@ func (t *ActivityTimer) SetTimeout(timeout time.Duration) { t.mu.Lock() defer t.mu.Unlock() - // only in initial - if t.checkTask != nil { - t.checkTask.Close() - } + common.CloseIfExists(t.checkTask) t.checkTask = newCheckTask t.Update() common.Must(newCheckTask.Start()) From be3e121429cf0ae99e56537731b2efd56df62692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?= Date: Tue, 26 Aug 2025 19:07:30 +0000 Subject: [PATCH 5/5] Change to atomic.Bool --- common/common.go | 1 - common/signal/timer.go | 17 ++++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/common/common.go b/common/common.go index a726ec1f27b4..f0134243c791 100644 --- a/common/common.go +++ b/common/common.go @@ -160,7 +160,6 @@ func CloseIfExists(obj any) error { if obj != nil { v := reflect.ValueOf(obj) if !v.IsNil() { - fmt.Println("closing", obj) return Close(obj) } } diff --git a/common/signal/timer.go b/common/signal/timer.go index da10d621d4cb..d5b35605c99d 100644 --- a/common/signal/timer.go +++ b/common/signal/timer.go @@ -3,6 +3,7 @@ package signal import ( "context" "sync" + "sync/atomic" "time" "github.com/xtls/xray-core/common" @@ -18,7 +19,7 @@ type ActivityTimer struct { updated chan struct{} checkTask *task.Periodic onTimeout func() - consumed bool + consumed atomic.Bool once sync.Once } @@ -40,17 +41,17 @@ func (t *ActivityTimer) check() error { func (t *ActivityTimer) finish() { t.once.Do(func() { + t.consumed.Store(true) t.mu.Lock() defer t.mu.Unlock() common.CloseIfExists(t.checkTask) t.onTimeout() - t.consumed = true }) } func (t *ActivityTimer) SetTimeout(timeout time.Duration) { - if t.consumed { + if t.consumed.Load() { return } if timeout == 0 { @@ -58,14 +59,16 @@ func (t *ActivityTimer) SetTimeout(timeout time.Duration) { return } + t.mu.Lock() + defer t.mu.Unlock() + // double check, just in case + if t.consumed.Load() { + return + } newCheckTask := &task.Periodic{ Interval: timeout, Execute: t.check, } - - t.mu.Lock() - defer t.mu.Unlock() - common.CloseIfExists(t.checkTask) t.checkTask = newCheckTask t.Update()