mirror of
https://github.com/OliveTin/OliveTin
synced 2026-08-28 21:46:37 +00:00
73 lines
1.8 KiB
Go
73 lines
1.8 KiB
Go
package executor
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// timeoutContext is a custom context that kills the process group when cancelled due to timeout.
|
|
type timeoutContext struct {
|
|
context.Context
|
|
cancel context.CancelFunc
|
|
logEntry *InternalLogEntry
|
|
process *os.Process
|
|
executor *Executor
|
|
processMu sync.Mutex
|
|
}
|
|
|
|
// newTimeoutContext creates a context that will kill the process group when the timeout expires.
|
|
// logEntry is the same InternalLogEntry as the running execution so Kill(logEntry) can use Attributes and Process.
|
|
// Pass nil logEntry for subprocesses (e.g. shellAfterCompleted); then setProcess stores the process and it is killed on timeout.
|
|
func newTimeoutContext(parent context.Context, timeout time.Duration, executor *Executor, logEntry *InternalLogEntry) (*timeoutContext, context.CancelFunc) {
|
|
ctx, cancel := context.WithTimeout(parent, timeout)
|
|
tc := &timeoutContext{
|
|
Context: ctx,
|
|
cancel: cancel,
|
|
logEntry: logEntry,
|
|
executor: executor,
|
|
}
|
|
|
|
go func() {
|
|
<-ctx.Done()
|
|
if ctx.Err() != context.DeadlineExceeded {
|
|
return
|
|
}
|
|
tc.processMu.Lock()
|
|
entry := tc.logEntry
|
|
process := tc.process
|
|
tc.processMu.Unlock()
|
|
if entry != nil {
|
|
_ = executor.Kill(entry)
|
|
} else if process != nil {
|
|
_ = executor.Kill(&InternalLogEntry{Process: process})
|
|
}
|
|
}()
|
|
|
|
return tc, cancel
|
|
}
|
|
|
|
func (tc *timeoutContext) setProcess(process *os.Process) {
|
|
tc.processMu.Lock()
|
|
if tc.logEntry != nil {
|
|
tc.logEntry.Process = process
|
|
} else {
|
|
tc.process = process
|
|
}
|
|
tc.processMu.Unlock()
|
|
|
|
tc.killProcessIfAlreadyTimedOut(process)
|
|
}
|
|
|
|
func (tc *timeoutContext) killProcessIfAlreadyTimedOut(process *os.Process) {
|
|
if tc.Context.Err() != context.DeadlineExceeded || process == nil {
|
|
return
|
|
}
|
|
if tc.logEntry != nil {
|
|
_ = tc.executor.Kill(tc.logEntry)
|
|
return
|
|
}
|
|
_ = tc.executor.Kill(&InternalLogEntry{Process: process})
|
|
}
|