mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-10-24 02:13:08 +08:00
198 lines
4.3 KiB
Go
198 lines
4.3 KiB
Go
package task
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"m7s.live/m7s/v5/pkg/util"
|
|
"reflect"
|
|
"slices"
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
var idG atomic.Uint32
|
|
|
|
func GetNextTaskID() uint32 {
|
|
return idG.Add(1)
|
|
}
|
|
|
|
// MarcoTask include sub tasks
|
|
type MarcoTask struct {
|
|
Task
|
|
addSub chan ITask
|
|
children []ITask
|
|
lazyRun sync.Once
|
|
childrenDisposed chan struct{}
|
|
childDisposeListeners []func(ITask)
|
|
blocked bool
|
|
}
|
|
|
|
func (*MarcoTask) GetTaskType() TaskType {
|
|
return TASK_TYPE_MACRO
|
|
}
|
|
|
|
func (mt *MarcoTask) getMarcoTask() *MarcoTask {
|
|
return mt
|
|
}
|
|
|
|
func (mt *MarcoTask) Blocked() bool {
|
|
return mt.blocked
|
|
}
|
|
|
|
func (mt *MarcoTask) waitChildrenDispose() {
|
|
close(mt.addSub)
|
|
<-mt.childrenDisposed
|
|
}
|
|
|
|
func (mt *MarcoTask) OnChildDispose(listener func(ITask)) {
|
|
mt.childDisposeListeners = append(mt.childDisposeListeners, listener)
|
|
}
|
|
|
|
func (mt *MarcoTask) onChildDispose(child ITask) {
|
|
for _, listener := range mt.childDisposeListeners {
|
|
listener(child)
|
|
}
|
|
if mt.parent != nil {
|
|
mt.parent.onChildDispose(child)
|
|
}
|
|
if child.getParent() == mt {
|
|
child.dispose()
|
|
}
|
|
}
|
|
|
|
func (mt *MarcoTask) dispose() {
|
|
if mt.childrenDisposed != nil {
|
|
mt.OnBeforeDispose(mt.waitChildrenDispose)
|
|
}
|
|
mt.Task.dispose()
|
|
}
|
|
|
|
func (mt *MarcoTask) RangeSubTask(callback func(task ITask) bool) {
|
|
for _, task := range mt.children {
|
|
callback(task)
|
|
}
|
|
}
|
|
|
|
func (mt *MarcoTask) AddTaskLazy(t IMarcoTask) {
|
|
t.GetTask().parent = mt
|
|
}
|
|
|
|
func (mt *MarcoTask) AddTask(t ITask, opt ...any) (task *Task) {
|
|
mt.lazyRun.Do(func() {
|
|
if mt.parent != nil && mt.handler == nil {
|
|
mt.parent.AddTask(mt)
|
|
}
|
|
mt.childrenDisposed = make(chan struct{})
|
|
mt.addSub = make(chan ITask, 10)
|
|
go mt.run()
|
|
})
|
|
if task = t.GetTask(); task.handler == nil {
|
|
task.parentCtx = mt.Context
|
|
for _, o := range opt {
|
|
switch v := o.(type) {
|
|
case context.Context:
|
|
task.parentCtx = v
|
|
case Description:
|
|
task.Description = v
|
|
case RetryConfig:
|
|
task.retry = v
|
|
case *slog.Logger:
|
|
task.Logger = v
|
|
}
|
|
}
|
|
if task.parentCtx == nil {
|
|
panic("context is nil")
|
|
}
|
|
task.parent = mt
|
|
task.level = mt.level + 1
|
|
if task.ID == 0 {
|
|
task.ID = GetNextTaskID()
|
|
}
|
|
task.Context, task.CancelCauseFunc = context.WithCancelCause(task.parentCtx)
|
|
task.startup = util.NewPromise(task.Context)
|
|
task.shutdown = util.NewPromise(context.Background())
|
|
task.handler = t
|
|
if task.Logger == nil {
|
|
task.Logger = mt.Logger
|
|
}
|
|
}
|
|
if mt.IsStopped() {
|
|
task.startup.Reject(mt.StopReason())
|
|
return
|
|
}
|
|
|
|
mt.addSub <- t
|
|
return
|
|
}
|
|
|
|
func (mt *MarcoTask) Call(callback func() error) {
|
|
mt.Post(callback).WaitStarted()
|
|
}
|
|
|
|
func (mt *MarcoTask) Post(callback func() error) *Task {
|
|
task := CreateTaskByCallBack(callback, nil)
|
|
return mt.AddTask(task)
|
|
}
|
|
|
|
func (mt *MarcoTask) addChild(task ITask) int {
|
|
mt.children = append(mt.children, task)
|
|
return len(mt.children) - 1
|
|
}
|
|
|
|
func (mt *MarcoTask) removeChild(index int) {
|
|
mt.onChildDispose(mt.children[index])
|
|
mt.children = slices.Delete(mt.children, index, index+1)
|
|
}
|
|
|
|
func (mt *MarcoTask) run() {
|
|
cases := []reflect.SelectCase{{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(mt.addSub)}}
|
|
defer func() {
|
|
err := recover()
|
|
if err != nil {
|
|
mt.Stop(err.(error))
|
|
}
|
|
stopReason := mt.StopReason()
|
|
for _, task := range mt.children {
|
|
task.Stop(stopReason)
|
|
mt.onChildDispose(task)
|
|
}
|
|
mt.children = nil
|
|
close(mt.childrenDisposed)
|
|
}()
|
|
for {
|
|
mt.blocked = false
|
|
if chosen, rev, ok := reflect.Select(cases); chosen == 0 {
|
|
mt.blocked = true
|
|
if !ok {
|
|
return
|
|
}
|
|
if task := rev.Interface().(ITask); task.getParent() == mt {
|
|
index := mt.addChild(task)
|
|
if err := task.start(); err == nil {
|
|
cases = append(cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(task.GetSignal())})
|
|
} else {
|
|
task.Stop(err)
|
|
mt.removeChild(index)
|
|
}
|
|
} else {
|
|
mt.addChild(task)
|
|
cases = append(cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(task.GetSignal())})
|
|
}
|
|
} else {
|
|
taskIndex := chosen - 1
|
|
task := mt.children[taskIndex]
|
|
switch tt := task.(type) {
|
|
case IChannelTask:
|
|
tt.Tick(rev.Interface())
|
|
}
|
|
if !ok {
|
|
mt.removeChild(taskIndex)
|
|
cases = slices.Delete(cases, chosen, chosen+1)
|
|
}
|
|
}
|
|
if !mt.handler.keepalive() && len(mt.children) == 0 {
|
|
mt.Stop(ErrAutoStop)
|
|
}
|
|
}
|
|
}
|