All checks were successful
		
		
	
	Build Docker and Deploy / Run goext test-suite (push) Successful in 3m31s
				
			
		
			
				
	
	
		
			133 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package dataext
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"git.blackforestbytes.com/BlackForestBytes/goext/langext"
 | 
						|
	"git.blackforestbytes.com/BlackForestBytes/goext/syncext"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
type DelayedCombiningInvoker struct {
 | 
						|
	syncLock           sync.Mutex
 | 
						|
	triggerChan        chan bool
 | 
						|
	cancelChan         chan bool
 | 
						|
	execNowChan        chan bool
 | 
						|
	action             func()
 | 
						|
	delay              time.Duration
 | 
						|
	maxDelay           time.Duration
 | 
						|
	executorRunning    *syncext.AtomicBool
 | 
						|
	lastRequestTime    time.Time
 | 
						|
	initialRequestTime time.Time
 | 
						|
}
 | 
						|
 | 
						|
func NewDelayedCombiningInvoker(action func(), delay time.Duration, maxDelay time.Duration) *DelayedCombiningInvoker {
 | 
						|
	return &DelayedCombiningInvoker{
 | 
						|
		action:             action,
 | 
						|
		delay:              delay,
 | 
						|
		maxDelay:           maxDelay,
 | 
						|
		executorRunning:    syncext.NewAtomicBool(false),
 | 
						|
		triggerChan:        make(chan bool),
 | 
						|
		cancelChan:         make(chan bool, 1),
 | 
						|
		execNowChan:        make(chan bool, 1),
 | 
						|
		lastRequestTime:    time.Now(),
 | 
						|
		initialRequestTime: time.Now(),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (d *DelayedCombiningInvoker) Request() {
 | 
						|
	now := time.Now()
 | 
						|
 | 
						|
	d.syncLock.Lock()
 | 
						|
	defer d.syncLock.Unlock()
 | 
						|
 | 
						|
	if d.executorRunning.Get() {
 | 
						|
		d.lastRequestTime = now
 | 
						|
 | 
						|
		d.triggerChan <- true
 | 
						|
	} else {
 | 
						|
		d.initialRequestTime = now
 | 
						|
		d.lastRequestTime = now
 | 
						|
 | 
						|
		d.executorRunning.Set(true)
 | 
						|
		syncext.ReadNonBlocking(d.triggerChan) // clear the channel
 | 
						|
		syncext.ReadNonBlocking(d.cancelChan)  // clear the channel
 | 
						|
		syncext.ReadNonBlocking(d.execNowChan) // clear the channel
 | 
						|
		go d.run()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (d *DelayedCombiningInvoker) run() {
 | 
						|
	defer func() {
 | 
						|
		d.syncLock.Lock()
 | 
						|
		d.executorRunning.Set(false)
 | 
						|
		d.syncLock.Unlock()
 | 
						|
	}()
 | 
						|
 | 
						|
	for {
 | 
						|
		d.syncLock.Lock()
 | 
						|
		timeOut := min(d.maxDelay-time.Since(d.initialRequestTime), d.delay-time.Since(d.lastRequestTime))
 | 
						|
		if timeOut < 0 {
 | 
						|
			timeOut = 0
 | 
						|
		}
 | 
						|
		d.syncLock.Unlock()
 | 
						|
 | 
						|
		immediately := false
 | 
						|
 | 
						|
		select {
 | 
						|
		case <-d.execNowChan:
 | 
						|
			// run immediately
 | 
						|
			immediately = true
 | 
						|
			break
 | 
						|
		case <-d.triggerChan:
 | 
						|
			// external trigger - needs to re-evaluate
 | 
						|
			break
 | 
						|
		case <-d.cancelChan:
 | 
						|
			// cancel
 | 
						|
			return
 | 
						|
		case <-time.After(timeOut):
 | 
						|
			// time elapsed - check for execution
 | 
						|
			break
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		d.syncLock.Lock()
 | 
						|
		execute := immediately || time.Since(d.lastRequestTime) >= d.delay || time.Since(d.initialRequestTime) >= d.maxDelay
 | 
						|
		if !execute {
 | 
						|
			d.syncLock.Unlock()
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		_ = langext.RunPanicSafe(d.action)
 | 
						|
		d.syncLock.Unlock()
 | 
						|
		return
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (d *DelayedCombiningInvoker) CancelPendingRequests() {
 | 
						|
	d.syncLock.Lock()
 | 
						|
	defer d.syncLock.Unlock()
 | 
						|
 | 
						|
	syncext.WriteNonBlocking(d.cancelChan, true)
 | 
						|
}
 | 
						|
 | 
						|
func (d *DelayedCombiningInvoker) HasPendingRequests() bool {
 | 
						|
	return d.executorRunning.Get()
 | 
						|
}
 | 
						|
 | 
						|
func (d *DelayedCombiningInvoker) ExecuteNow() bool {
 | 
						|
	d.syncLock.Lock()
 | 
						|
	defer d.syncLock.Unlock()
 | 
						|
 | 
						|
	if d.executorRunning.Get() {
 | 
						|
		syncext.WriteNonBlocking(d.execNowChan, true)
 | 
						|
		return true
 | 
						|
	} else {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (d *DelayedCombiningInvoker) WaitForCompletion(ctx context.Context) error {
 | 
						|
	return d.executorRunning.WaitWithContext(ctx, false)
 | 
						|
}
 |