status-go/server/timeout.go

95 lines
2.4 KiB
Go

package server
import (
"sync"
"time"
)
// timeoutManager represents a discrete encapsulation of timeout functionality.
// this struct expose 3 functions:
// - SetTimeout
// - StartTimeout
// - StopTimeout
type timeoutManager struct {
// timeout number of milliseconds the timeout operation will run before executing the `terminate` func()
// 0 represents an inactive timeout
timeout uint
// exitQueue handles the cancel signal channels that circumvent timeout operations and prevent the
// execution of any `terminate` func()
exitQueue *exitQueueManager
}
// newTimeoutManager returns a fully qualified and initialised timeoutManager
func newTimeoutManager() *timeoutManager {
return &timeoutManager{
exitQueue: &exitQueueManager{queue: []chan struct{}{}},
}
}
// SetTimeout sets the value of the timeoutManager.timeout
func (t *timeoutManager) SetTimeout(milliseconds uint) {
t.timeout = milliseconds
}
// StartTimeout starts a timeout operation based on the set timeoutManager.timeout value
// the given terminate func() will be executed once the timeout duration has passed
func (t *timeoutManager) StartTimeout(terminate func()) {
if t.timeout == 0 {
return
}
t.StopTimeout()
exit := make(chan struct{}, 1)
t.exitQueue.add(exit)
go t.run(terminate, exit)
}
// StopTimeout terminates a timeout operation and exits gracefully
func (t *timeoutManager) StopTimeout() {
if t.timeout == 0 {
return
}
t.exitQueue.empty()
}
// run inits the main timeout run function that awaits for the exit command to be triggered or for the
// timeout duration to elapse and trigger the parameter terminate function.
func (t *timeoutManager) run(terminate func(), exit chan struct{}) {
select {
case <-exit:
return
case <-time.After(time.Duration(t.timeout) * time.Millisecond):
terminate()
// TODO fire signal to let UI know
// https://github.com/status-im/status-go/issues/3305
return
}
}
// exitQueueManager
type exitQueueManager struct {
queue []chan struct{}
queueLock sync.Mutex
}
// add handles new exit channels adding them to the exit queue
func (e *exitQueueManager) add(exit chan struct{}) {
e.queueLock.Lock()
defer e.queueLock.Unlock()
e.queue = append(e.queue, exit)
}
// empty sends a signal to every exit channel in the queue and then resets the queue
func (e *exitQueueManager) empty() {
e.queueLock.Lock()
defer e.queueLock.Unlock()
for i := range e.queue {
e.queue[i] <- struct{}{}
}
e.queue = []chan struct{}{}
}