|
|
@@ -3,6 +3,7 @@ package signal
|
|
|
import (
|
|
|
"context"
|
|
|
"sync"
|
|
|
+ "sync/atomic"
|
|
|
"time"
|
|
|
|
|
|
"github.com/xtls/xray-core/common"
|
|
|
@@ -14,10 +15,12 @@ type ActivityUpdater interface {
|
|
|
}
|
|
|
|
|
|
type ActivityTimer struct {
|
|
|
- sync.RWMutex
|
|
|
+ mu sync.RWMutex
|
|
|
updated chan struct{}
|
|
|
checkTask *task.Periodic
|
|
|
onTimeout func()
|
|
|
+ consumed atomic.Bool
|
|
|
+ once sync.Once
|
|
|
}
|
|
|
|
|
|
func (t *ActivityTimer) Update() {
|
|
|
@@ -37,39 +40,39 @@ func (t *ActivityTimer) check() error {
|
|
|
}
|
|
|
|
|
|
func (t *ActivityTimer) finish() {
|
|
|
- t.Lock()
|
|
|
- defer t.Unlock()
|
|
|
+ t.once.Do(func() {
|
|
|
+ t.consumed.Store(true)
|
|
|
+ t.mu.Lock()
|
|
|
+ defer t.mu.Unlock()
|
|
|
|
|
|
- if t.onTimeout != nil {
|
|
|
+ common.CloseIfExists(t.checkTask)
|
|
|
t.onTimeout()
|
|
|
- t.onTimeout = nil
|
|
|
- }
|
|
|
- if t.checkTask != nil {
|
|
|
- t.checkTask.Close()
|
|
|
- t.checkTask = nil
|
|
|
- }
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
|
|
|
+ if t.consumed.Load() {
|
|
|
+ return
|
|
|
+ }
|
|
|
if timeout == 0 {
|
|
|
t.finish()
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- checkTask := &task.Periodic{
|
|
|
+ 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.Lock()
|
|
|
-
|
|
|
- if t.checkTask != nil {
|
|
|
- t.checkTask.Close()
|
|
|
- }
|
|
|
- t.checkTask = checkTask
|
|
|
+ common.CloseIfExists(t.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 {
|