Add Run control event stream
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
"browser.local/platform/validator"
|
||||
)
|
||||
|
||||
const defaultRunControlEventRetrySeconds = 5
|
||||
|
||||
type RunControlEventSubscription struct {
|
||||
Initial *domain.RunControlEvent
|
||||
Events <-chan domain.RunControlEvent
|
||||
Cancel func()
|
||||
}
|
||||
|
||||
func (svc *CoreService) SubscribeRunControlEvents(request domain.RunControlStreamRequest) (RunControlEventSubscription, error) {
|
||||
request = domain.CopyRunControlStreamRequest(request)
|
||||
if err := validator.ValidateRunControlStreamRequest(request); err != nil {
|
||||
return RunControlEventSubscription{}, err
|
||||
}
|
||||
if err := svc.validateRunSession(request.RunEndpointID, request.SessionToken); err != nil {
|
||||
return RunControlEventSubscription{}, err
|
||||
}
|
||||
|
||||
updates := make(chan domain.RunControlEvent, 8)
|
||||
var initial *domain.RunControlEvent
|
||||
svc.controlStreamMu.Lock()
|
||||
if latest, exists := svc.controlStreamEvents[request.RunEndpointID]; exists && latest.Sequence > request.LastEventSeq {
|
||||
copy := domain.CopyRunControlEvent(latest)
|
||||
initial = ©
|
||||
}
|
||||
svc.controlStreamWaiters[request.RunEndpointID] = append(svc.controlStreamWaiters[request.RunEndpointID], updates)
|
||||
svc.controlStreamMu.Unlock()
|
||||
|
||||
var once sync.Once
|
||||
cancel := func() {
|
||||
once.Do(func() {
|
||||
svc.controlStreamMu.Lock()
|
||||
waiters := svc.controlStreamWaiters[request.RunEndpointID]
|
||||
for index, candidate := range waiters {
|
||||
if candidate == updates {
|
||||
waiters = append(waiters[:index], waiters[index+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(waiters) == 0 {
|
||||
delete(svc.controlStreamWaiters, request.RunEndpointID)
|
||||
} else {
|
||||
svc.controlStreamWaiters[request.RunEndpointID] = waiters
|
||||
}
|
||||
svc.controlStreamMu.Unlock()
|
||||
close(updates)
|
||||
})
|
||||
}
|
||||
return RunControlEventSubscription{Initial: initial, Events: updates, Cancel: cancel}, nil
|
||||
}
|
||||
|
||||
func (svc *CoreService) publishRunControlEvent(runEndpointID string, eventType string) {
|
||||
if runEndpointID == "" || eventType == "" {
|
||||
return
|
||||
}
|
||||
svc.controlStreamMu.Lock()
|
||||
sequence := svc.controlStreamSeq[runEndpointID] + 1
|
||||
svc.controlStreamSeq[runEndpointID] = sequence
|
||||
event := domain.RunControlEvent{RunEndpointID: runEndpointID, Sequence: sequence, Type: eventType, ServerTime: svc.now(), RetrySeconds: defaultRunControlEventRetrySeconds}
|
||||
svc.controlStreamEvents[runEndpointID] = event
|
||||
waiters := append([]chan domain.RunControlEvent(nil), svc.controlStreamWaiters[runEndpointID]...)
|
||||
svc.controlStreamMu.Unlock()
|
||||
for _, waiter := range waiters {
|
||||
select {
|
||||
case waiter <- event:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user