3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-22 20:09:41 +01:00

Possible IdleTimer lock fix

This commit is contained in:
Daniel Oaks 2018-01-30 14:26:29 +10:00
parent cabb3b219d
commit 7b88d21e58

View File

@ -37,29 +37,42 @@ type IdleTimer struct {
sync.Mutex // tier 1 sync.Mutex // tier 1
// immutable after construction // immutable after construction
registerTimeout time.Duration registerTimeout time.Duration
idleTimeout time.Duration quitTimeout time.Duration
idleTimeoutWithResume time.Duration client *Client
quitTimeout time.Duration
client *Client
// mutable // mutable
state TimerState idleTimeout time.Duration
timer *time.Timer state TimerState
timer *time.Timer
} }
// NewIdleTimer sets up a new IdleTimer using constant timeouts. // NewIdleTimer sets up a new IdleTimer using constant timeouts.
func NewIdleTimer(client *Client) *IdleTimer { func NewIdleTimer(client *Client) *IdleTimer {
it := IdleTimer{ it := IdleTimer{
registerTimeout: RegisterTimeout, registerTimeout: RegisterTimeout,
idleTimeout: IdleTimeout, idleTimeout: IdleTimeout,
idleTimeoutWithResume: IdleTimeoutWithResumeCap, quitTimeout: QuitTimeout,
quitTimeout: QuitTimeout, client: client,
client: client,
} }
return &it return &it
} }
// updateIdleDuration updates the idle duration, given the client's caps.
func (it *IdleTimer) updateIdleDuration() {
newIdleTime := IdleTimeout
// if they have the resume cap, wait longer before pinging them out
// to give them a chance to resume their connection
if it.client.capabilities.Has(caps.Resume) {
newIdleTime = IdleTimeoutWithResumeCap
}
it.Lock()
defer it.Unlock()
it.idleTimeout = newIdleTime
}
// Start starts counting idle time; if there is no activity from the client, // Start starts counting idle time; if there is no activity from the client,
// it will eventually be stopped. // it will eventually be stopped.
func (it *IdleTimer) Start() { func (it *IdleTimer) Start() {
@ -75,6 +88,8 @@ func (it *IdleTimer) Touch() {
return return
} }
it.updateIdleDuration()
it.Lock() it.Lock()
defer it.Unlock() defer it.Unlock()
// a touch transitions TimerUnregistered or TimerIdle into TimerActive // a touch transitions TimerUnregistered or TimerIdle into TimerActive
@ -85,6 +100,8 @@ func (it *IdleTimer) Touch() {
} }
func (it *IdleTimer) processTimeout() { func (it *IdleTimer) processTimeout() {
it.updateIdleDuration()
var previousState TimerState var previousState TimerState
func() { func() {
it.Lock() it.Lock()
@ -125,13 +142,7 @@ func (it *IdleTimer) resetTimeout() {
case TimerUnregistered: case TimerUnregistered:
nextTimeout = it.registerTimeout nextTimeout = it.registerTimeout
case TimerActive: case TimerActive:
// if they have the resume cap, wait longer before pinging them out nextTimeout = it.idleTimeout
// to give them a chance to resume their connection
if it.client.capabilities.Has(caps.Resume) {
nextTimeout = it.idleTimeoutWithResume
} else {
nextTimeout = it.idleTimeout
}
case TimerIdle: case TimerIdle:
nextTimeout = it.quitTimeout nextTimeout = it.quitTimeout
case TimerDead: case TimerDead:
@ -146,6 +157,8 @@ func (it *IdleTimer) quitMessage(state TimerState) string {
return fmt.Sprintf("Registration timeout: %v", it.registerTimeout) return fmt.Sprintf("Registration timeout: %v", it.registerTimeout)
case TimerIdle: case TimerIdle:
// how many seconds before registered clients are timed out (IdleTimeout plus QuitTimeout). // how many seconds before registered clients are timed out (IdleTimeout plus QuitTimeout).
it.Lock()
defer it.Unlock()
return fmt.Sprintf("Ping timeout: %v", (it.idleTimeout + it.quitTimeout)) return fmt.Sprintf("Ping timeout: %v", (it.idleTimeout + it.quitTimeout))
default: default:
// shouldn't happen // shouldn't happen