github.com/status-im/status-go@v1.1.0/server/timeout.go (about)

     1  package server
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  )
     7  
     8  // timeoutManager represents a discrete encapsulation of timeout functionality.
     9  // this struct expose 3 functions:
    10  //   - SetTimeout
    11  //   - StartTimeout
    12  //   - StopTimeout
    13  type timeoutManager struct {
    14  	// timeout number of milliseconds the timeout operation will run before executing the `terminate` func()
    15  	// 0 represents an inactive timeout
    16  	timeout uint
    17  
    18  	// exitQueue handles the cancel signal channels that circumvent timeout operations and prevent the
    19  	// execution of any `terminate` func()
    20  	exitQueue *exitQueueManager
    21  }
    22  
    23  // newTimeoutManager returns a fully qualified and initialised timeoutManager
    24  func newTimeoutManager() *timeoutManager {
    25  	return &timeoutManager{
    26  		exitQueue: &exitQueueManager{queue: []chan struct{}{}},
    27  	}
    28  }
    29  
    30  // SetTimeout sets the value of the timeoutManager.timeout
    31  func (t *timeoutManager) SetTimeout(milliseconds uint) {
    32  	t.timeout = milliseconds
    33  }
    34  
    35  // StartTimeout starts a timeout operation based on the set timeoutManager.timeout value
    36  // the given terminate func() will be executed once the timeout duration has passed
    37  func (t *timeoutManager) StartTimeout(terminate func()) {
    38  	if t.timeout == 0 {
    39  		return
    40  	}
    41  	t.StopTimeout()
    42  
    43  	exit := make(chan struct{}, 1)
    44  	t.exitQueue.add(exit)
    45  	go t.run(terminate, exit)
    46  }
    47  
    48  // StopTimeout terminates a timeout operation and exits gracefully
    49  func (t *timeoutManager) StopTimeout() {
    50  	if t.timeout == 0 {
    51  		return
    52  	}
    53  	t.exitQueue.empty()
    54  }
    55  
    56  // run inits the main timeout run function that awaits for the exit command to be triggered or for the
    57  // timeout duration to elapse and trigger the parameter terminate function.
    58  func (t *timeoutManager) run(terminate func(), exit chan struct{}) {
    59  	select {
    60  	case <-exit:
    61  		return
    62  	case <-time.After(time.Duration(t.timeout) * time.Millisecond):
    63  		terminate()
    64  		// TODO fire signal to let UI know
    65  		//  https://github.com/status-im/status-go/issues/3305
    66  		return
    67  	}
    68  }
    69  
    70  // exitQueueManager
    71  type exitQueueManager struct {
    72  	queue     []chan struct{}
    73  	queueLock sync.Mutex
    74  }
    75  
    76  // add handles new exit channels adding them to the exit queue
    77  func (e *exitQueueManager) add(exit chan struct{}) {
    78  	e.queueLock.Lock()
    79  	defer e.queueLock.Unlock()
    80  
    81  	e.queue = append(e.queue, exit)
    82  }
    83  
    84  // empty sends a signal to every exit channel in the queue and then resets the queue
    85  func (e *exitQueueManager) empty() {
    86  	e.queueLock.Lock()
    87  	defer e.queueLock.Unlock()
    88  
    89  	for i := range e.queue {
    90  		e.queue[i] <- struct{}{}
    91  	}
    92  
    93  	e.queue = []chan struct{}{}
    94  }