77 lines
1.8 KiB
Go
77 lines
1.8 KiB
Go
package orbit
|
||
|
||
import (
|
||
"context"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
// executionGuard 独立于网络控制取消处理器,已到期/取消的上下文永不恢复。
|
||
type executionGuard struct {
|
||
ctx context.Context
|
||
cancel context.CancelCauseFunc
|
||
mu sync.Mutex
|
||
deadline time.Time
|
||
wake chan struct{}
|
||
done chan struct{}
|
||
}
|
||
|
||
func newExecutionGuard(parent context.Context, lease WorkerLease) (*executionGuard, bool) {
|
||
deadline, ok := lease.ExecutionDeadline()
|
||
if !ok {
|
||
return nil, false
|
||
}
|
||
ctx, cancel := context.WithCancelCause(parent)
|
||
g := &executionGuard{ctx: ctx, cancel: cancel, deadline: deadline, wake: make(chan struct{}, 1), done: make(chan struct{})}
|
||
go g.watch()
|
||
return g, true
|
||
}
|
||
func (g *executionGuard) watch() {
|
||
defer close(g.done)
|
||
timer := time.NewTimer(time.Until(g.until()))
|
||
defer timer.Stop()
|
||
for {
|
||
select {
|
||
case <-g.ctx.Done():
|
||
return
|
||
case <-g.wake:
|
||
case <-timer.C:
|
||
}
|
||
g.mu.Lock()
|
||
remaining := time.Until(g.deadline)
|
||
if remaining <= 0 {
|
||
g.cancel(ErrExecutionLeaseLost)
|
||
g.mu.Unlock()
|
||
return
|
||
}
|
||
timer.Reset(remaining)
|
||
g.mu.Unlock()
|
||
}
|
||
}
|
||
func (g *executionGuard) update(lease WorkerLease) bool {
|
||
g.mu.Lock()
|
||
defer g.mu.Unlock()
|
||
if g.ctx.Err() != nil || !time.Now().Before(g.deadline) {
|
||
g.cancel(ErrExecutionLeaseLost)
|
||
return false
|
||
}
|
||
deadline, ok := lease.ExecutionDeadline()
|
||
if !lease.Active || lease.State != "RUNNING" {
|
||
g.cancel(ErrExecutionLeaseLost)
|
||
return false
|
||
}
|
||
// Lease 是新鲜核验,不是历史 Receipt。新会话可能缩短有效窗口,必须遵守。
|
||
if !ok {
|
||
g.cancel(ErrExecutionLeaseLost)
|
||
return false
|
||
}
|
||
g.deadline = deadline
|
||
select {
|
||
case g.wake <- struct{}{}:
|
||
default:
|
||
}
|
||
return true
|
||
}
|
||
func (g *executionGuard) until() time.Time { g.mu.Lock(); defer g.mu.Unlock(); return g.deadline }
|
||
func (g *executionGuard) stop(cause error) { g.cancel(cause); <-g.done }
|