go.temporal.io/server@v1.23.0/common/timer/remote_gate.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package timer
    26  
    27  import (
    28  	"sync"
    29  	"time"
    30  )
    31  
    32  type (
    33  	RemoteGate interface {
    34  		Gate
    35  		// SetCurrentTime set the current time, and additionally fire the fire chan
    36  		// if new "current" time is after the next wakeup time, return true if
    37  		// "current" is actually updated
    38  		SetCurrentTime(nextTime time.Time) bool
    39  	}
    40  
    41  	RemoteGateImpl struct {
    42  		// the channel which will be used to proxy the fired timer
    43  		fireCh chan struct{}
    44  
    45  		// lock for timer and next wakeup time
    46  		sync.Mutex
    47  		// view of current time
    48  		currentTime time.Time
    49  		// time which timer will fire
    50  		nextWakeupTime time.Time
    51  	}
    52  )
    53  
    54  // NewRemoteGate create a new timer gate instance
    55  func NewRemoteGate() RemoteGate {
    56  	rg := &RemoteGateImpl{
    57  		currentTime:    time.Time{},
    58  		nextWakeupTime: time.Time{},
    59  		fireCh:         make(chan struct{}, 1),
    60  	}
    61  	return rg
    62  }
    63  
    64  // FireCh return the channel which will be fired when time is up
    65  func (rg *RemoteGateImpl) FireCh() <-chan struct{} {
    66  	return rg.fireCh
    67  }
    68  
    69  // FireAfter check will the timer get fired after a certain time
    70  func (rg *RemoteGateImpl) FireAfter(now time.Time) bool {
    71  	rg.Lock()
    72  	defer rg.Unlock()
    73  
    74  	active := rg.currentTime.Before(rg.nextWakeupTime)
    75  	return active && rg.nextWakeupTime.After(now)
    76  }
    77  
    78  // Update the timer gate, return true if update is a success.
    79  // Success means timer is idle or timer is set with a sooner time to fire
    80  func (rg *RemoteGateImpl) Update(nextTime time.Time) bool {
    81  	rg.Lock()
    82  	defer rg.Unlock()
    83  
    84  	active := rg.currentTime.Before(rg.nextWakeupTime)
    85  	if active {
    86  		if rg.nextWakeupTime.Before(nextTime) {
    87  			// current time < next wake up time < next time
    88  			return false
    89  		}
    90  
    91  		if rg.currentTime.Before(nextTime) {
    92  			// current time < next time <= next wake-up time
    93  			rg.nextWakeupTime = nextTime
    94  			return true
    95  		}
    96  
    97  		// next time <= current time < next wake-up time
    98  		rg.nextWakeupTime = nextTime
    99  		rg.fire()
   100  		return true
   101  	}
   102  
   103  	// this means the timer, before stopped, has already fired / never active
   104  	if !rg.currentTime.Before(nextTime) {
   105  		// next time is <= current time, need to fire immediately
   106  		// whether to update next wake-up time or not is irrelevant
   107  		rg.fire()
   108  	} else {
   109  		// next time > current time
   110  		rg.nextWakeupTime = nextTime
   111  	}
   112  	return true
   113  }
   114  
   115  // Close shutdown the timer
   116  func (rg *RemoteGateImpl) Close() {
   117  	// no op
   118  }
   119  
   120  // SetCurrentTime set the current time, and additionally fire the fire chan
   121  // if new "current" time is after the next wake-up time, return true if
   122  // "current" is actually updated
   123  func (rg *RemoteGateImpl) SetCurrentTime(currentTime time.Time) bool {
   124  	rg.Lock()
   125  	defer rg.Unlock()
   126  
   127  	if !rg.currentTime.Before(currentTime) {
   128  		// new current time is <= current time
   129  		return false
   130  	}
   131  
   132  	// NOTE: do not update the current time now
   133  	if !rg.currentTime.Before(rg.nextWakeupTime) {
   134  		// current time already >= next wakeup time
   135  		// avoid duplicate fire
   136  		rg.currentTime = currentTime
   137  		return true
   138  	}
   139  
   140  	rg.currentTime = currentTime
   141  	if !rg.currentTime.Before(rg.nextWakeupTime) {
   142  		rg.fire()
   143  	}
   144  	return true
   145  }
   146  
   147  func (rg *RemoteGateImpl) fire() {
   148  	select {
   149  	case rg.fireCh <- struct{}{}:
   150  		// timer successfully triggered
   151  	default:
   152  		// timer already triggered, pass
   153  	}
   154  }