mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
177 lines
4.7 KiB
Go
177 lines
4.7 KiB
Go
//go:build linux
|
|
|
|
package scenario
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
ErrInvalidHeldSessionPlan = errors.New("held session plan is invalid")
|
|
ErrHeldSessionClosedBeforeLive = errors.New("held session closed before live")
|
|
ErrHeldSessionLiveResolved = errors.New("held session live state already resolved")
|
|
)
|
|
|
|
type heldSession interface {
|
|
Plan() StressSessionPlan
|
|
WaitLive(context.Context) error
|
|
Close(context.Context) error
|
|
WaitClosed(context.Context) error
|
|
IOStreamID() (string, bool)
|
|
Done() <-chan struct{}
|
|
CloseResult() error
|
|
}
|
|
|
|
type heldSessionState uint8
|
|
|
|
const (
|
|
heldSessionConstructed heldSessionState = iota
|
|
heldSessionLive
|
|
heldSessionFailed
|
|
heldSessionClosing
|
|
heldSessionClosed
|
|
)
|
|
|
|
type heldSessionLifecycle struct {
|
|
baseContext context.Context
|
|
plan StressSessionPlan
|
|
ioStreamID string
|
|
cleanupTimeout time.Duration
|
|
|
|
mu sync.Mutex
|
|
state heldSessionState
|
|
liveResult error
|
|
liveDone chan struct{}
|
|
closedDone chan struct{}
|
|
closedResult error
|
|
}
|
|
|
|
type heldSessionCloseOwner struct {
|
|
lifecycle *heldSessionLifecycle
|
|
closeOnce sync.Once
|
|
}
|
|
|
|
func newHeldSessionLifecycle(baseContext context.Context, plan StressSessionPlan, ioStreamID string, cleanupTimeout time.Duration) (*heldSessionLifecycle, error) {
|
|
if baseContext == nil || plan.ID.String() == "" || !supportedHeldSessionKind(plan.Kind) || plan.Ordinal < 1 || plan.Agent.Int() < 1 || cleanupTimeout <= 0 {
|
|
return nil, ErrInvalidHeldSessionPlan
|
|
}
|
|
return &heldSessionLifecycle{
|
|
baseContext: baseContext,
|
|
plan: plan,
|
|
ioStreamID: ioStreamID,
|
|
cleanupTimeout: cleanupTimeout,
|
|
state: heldSessionConstructed,
|
|
liveDone: make(chan struct{}),
|
|
closedDone: make(chan struct{}),
|
|
}, nil
|
|
}
|
|
|
|
func supportedHeldSessionKind(kind StressSessionKind) bool {
|
|
switch kind {
|
|
case StressSessionTerminal, StressSessionNAT, StressSessionFM:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (lifecycle *heldSessionLifecycle) Plan() StressSessionPlan { return lifecycle.plan }
|
|
|
|
func (lifecycle *heldSessionLifecycle) IOStreamID() (string, bool) {
|
|
return lifecycle.ioStreamID, lifecycle.ioStreamID != ""
|
|
}
|
|
|
|
func (lifecycle *heldSessionLifecycle) setIOStreamID(streamID string) error {
|
|
if streamID == "" {
|
|
return errors.New("held session stream ID is empty")
|
|
}
|
|
lifecycle.mu.Lock()
|
|
defer lifecycle.mu.Unlock()
|
|
if lifecycle.state != heldSessionConstructed {
|
|
return ErrHeldSessionLiveResolved
|
|
}
|
|
lifecycle.ioStreamID = streamID
|
|
return nil
|
|
}
|
|
|
|
func (lifecycle *heldSessionLifecycle) markLive(err error) error {
|
|
lifecycle.mu.Lock()
|
|
defer lifecycle.mu.Unlock()
|
|
if lifecycle.state != heldSessionConstructed {
|
|
return ErrHeldSessionLiveResolved
|
|
}
|
|
lifecycle.liveResult = err
|
|
if err == nil {
|
|
lifecycle.state = heldSessionLive
|
|
} else {
|
|
lifecycle.state = heldSessionFailed
|
|
}
|
|
close(lifecycle.liveDone)
|
|
return nil
|
|
}
|
|
|
|
func (lifecycle *heldSessionLifecycle) WaitLive(ctx context.Context) error {
|
|
select {
|
|
case <-lifecycle.liveDone:
|
|
lifecycle.mu.Lock()
|
|
defer lifecycle.mu.Unlock()
|
|
return lifecycle.liveResult
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
|
|
func (lifecycle *heldSessionLifecycle) beginClose() (*heldSessionCloseOwner, bool) {
|
|
lifecycle.mu.Lock()
|
|
defer lifecycle.mu.Unlock()
|
|
if lifecycle.state == heldSessionConstructed {
|
|
lifecycle.liveResult = ErrHeldSessionClosedBeforeLive
|
|
lifecycle.state = heldSessionClosing
|
|
close(lifecycle.liveDone)
|
|
return &heldSessionCloseOwner{lifecycle: lifecycle}, true
|
|
}
|
|
if lifecycle.state == heldSessionLive || lifecycle.state == heldSessionFailed {
|
|
lifecycle.state = heldSessionClosing
|
|
return &heldSessionCloseOwner{lifecycle: lifecycle}, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func (owner *heldSessionCloseOwner) cleanupContext() (context.Context, context.CancelFunc) {
|
|
// The deadline only signals cancellation; it cannot forcibly terminate arbitrary cleanup work.
|
|
return context.WithTimeout(context.WithoutCancel(owner.lifecycle.baseContext), owner.lifecycle.cleanupTimeout)
|
|
}
|
|
|
|
func (owner *heldSessionCloseOwner) markClosed(err error) {
|
|
owner.closeOnce.Do(func() {
|
|
lifecycle := owner.lifecycle
|
|
lifecycle.mu.Lock()
|
|
lifecycle.closedResult = err
|
|
lifecycle.state = heldSessionClosed
|
|
close(lifecycle.closedDone)
|
|
lifecycle.mu.Unlock()
|
|
})
|
|
}
|
|
|
|
func (lifecycle *heldSessionLifecycle) WaitClosed(ctx context.Context) error {
|
|
select {
|
|
case <-lifecycle.closedDone:
|
|
lifecycle.mu.Lock()
|
|
defer lifecycle.mu.Unlock()
|
|
return lifecycle.closedResult
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
|
|
func (lifecycle *heldSessionLifecycle) Done() <-chan struct{} { return lifecycle.closedDone }
|
|
|
|
func (lifecycle *heldSessionLifecycle) CloseResult() error {
|
|
lifecycle.mu.Lock()
|
|
defer lifecycle.mu.Unlock()
|
|
return lifecycle.closedResult
|
|
}
|