3
0
mirror of https://github.com/ergochat/ergo.git synced 2025-01-03 16:42:38 +01:00

Merge pull request #171 from slingamn/atime

fix a race condition in client timeouts
This commit is contained in:
Daniel Oaks 2017-12-03 12:59:08 +10:00 committed by GitHub
commit b2e5738f08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 12 additions and 11 deletions

View File

@ -261,6 +261,8 @@ func (client *Client) run() {
// Active updates when the client was last 'active' (i.e. the user should be sitting in front of their client). // Active updates when the client was last 'active' (i.e. the user should be sitting in front of their client).
func (client *Client) Active() { func (client *Client) Active() {
client.stateMutex.Lock()
defer client.stateMutex.Unlock()
client.atime = time.Now() client.atime = time.Now()
} }
@ -298,6 +300,8 @@ func (client *Client) Register() {
// IdleTime returns how long this client's been idle. // IdleTime returns how long this client's been idle.
func (client *Client) IdleTime() time.Duration { func (client *Client) IdleTime() time.Duration {
client.stateMutex.RLock()
defer client.stateMutex.RUnlock()
return time.Since(client.atime) return time.Since(client.atime)
} }

View File

@ -38,7 +38,6 @@ type IdleTimer struct {
// mutable // mutable
client *Client client *Client
state TimerState
lastSeen time.Time lastSeen time.Time
} }
@ -49,7 +48,6 @@ func NewIdleTimer(client *Client) *IdleTimer {
idleTimeout: IdleTimeout, idleTimeout: IdleTimeout,
quitTimeout: QuitTimeout, quitTimeout: QuitTimeout,
client: client, client: client,
state: TimerUnregistered,
} }
return &it return &it
} }
@ -58,17 +56,18 @@ func NewIdleTimer(client *Client) *IdleTimer {
// it will eventually be stopped. // it will eventually be stopped.
func (it *IdleTimer) Start() { func (it *IdleTimer) Start() {
it.Lock() it.Lock()
it.state = TimerUnregistered
it.lastSeen = time.Now() it.lastSeen = time.Now()
it.Unlock() it.Unlock()
go it.mainLoop() go it.mainLoop()
} }
func (it *IdleTimer) mainLoop() { func (it *IdleTimer) mainLoop() {
state := TimerUnregistered
var lastPinged time.Time
for { for {
it.Lock() it.Lock()
client := it.client client := it.client
state := it.state
lastSeen := it.lastSeen lastSeen := it.lastSeen
it.Unlock() it.Unlock()
@ -76,7 +75,8 @@ func (it *IdleTimer) mainLoop() {
return return
} }
idleTime := time.Now().Sub(lastSeen) now := time.Now()
idleTime := now.Sub(lastSeen)
var nextSleep time.Duration var nextSleep time.Duration
if state == TimerUnregistered { if state == TimerUnregistered {
@ -87,8 +87,8 @@ func (it *IdleTimer) mainLoop() {
nextSleep = it.registerTimeout - idleTime nextSleep = it.registerTimeout - idleTime
} }
} else if state == TimerIdle { } else if state == TimerIdle {
if idleTime < it.quitTimeout { if lastSeen.After(lastPinged) {
// new ping came in after we transitioned to TimerIdle, // new pong came in after we transitioned to TimerIdle,
// transition back to active and process deadlines below // transition back to active and process deadlines below
state = TimerActive state = TimerActive
} else { } else {
@ -100,6 +100,7 @@ func (it *IdleTimer) mainLoop() {
nextSleep = it.idleTimeout - idleTime nextSleep = it.idleTimeout - idleTime
if nextSleep <= 0 { if nextSleep <= 0 {
state = TimerIdle state = TimerIdle
lastPinged = now
client.Ping() client.Ping()
// grant the client at least quitTimeout to respond // grant the client at least quitTimeout to respond
nextSleep = it.quitTimeout nextSleep = it.quitTimeout
@ -113,10 +114,6 @@ func (it *IdleTimer) mainLoop() {
return return
} }
it.Lock()
it.state = state
it.Unlock()
time.Sleep(nextSleep) time.Sleep(nextSleep)
} }
} }