mirror of
				https://github.com/langhuihui/monibuca.git
				synced 2025-11-01 00:52:36 +08:00 
			
		
		
		
	 8a9fffb987
			
		
	
	8a9fffb987
	
	
	
		
			
			- Refactor frame converter implementation - Update mp4 track to use ICodex - General refactoring and code improvements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			168 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package task
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"reflect"
 | |
| 	"runtime/debug"
 | |
| 	"slices"
 | |
| 	"sync"
 | |
| 	"sync/atomic"
 | |
| )
 | |
| 
 | |
| type Singleton[T comparable] struct {
 | |
| 	instance atomic.Value
 | |
| 	mux      sync.Mutex
 | |
| }
 | |
| 
 | |
| func (s *Singleton[T]) Load() T {
 | |
| 	return s.instance.Load().(T)
 | |
| }
 | |
| 
 | |
| func (s *Singleton[T]) Get(newF func() T) T {
 | |
| 	ch := s.instance.Load() //fast
 | |
| 	if ch == nil {          // slow
 | |
| 		s.mux.Lock()
 | |
| 		defer s.mux.Unlock()
 | |
| 		if ch = s.instance.Load(); ch == nil {
 | |
| 			ch = newF()
 | |
| 			s.instance.Store(ch)
 | |
| 		}
 | |
| 	}
 | |
| 	return ch.(T)
 | |
| }
 | |
| 
 | |
| type EventLoop struct {
 | |
| 	cases    []reflect.SelectCase
 | |
| 	children []ITask
 | |
| 	addSub   Singleton[chan any]
 | |
| 	running  atomic.Bool
 | |
| }
 | |
| 
 | |
| func (e *EventLoop) getInput() chan any {
 | |
| 	return e.addSub.Get(func() chan any {
 | |
| 		return make(chan any, 20)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (e *EventLoop) active(mt *Job) {
 | |
| 	if mt.parent != nil {
 | |
| 		mt.parent.eventLoop.active(mt.parent)
 | |
| 	}
 | |
| 	if e.running.CompareAndSwap(false, true) {
 | |
| 		go e.run(mt)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (e *EventLoop) add(mt *Job, sub any) (err error) {
 | |
| 	shouldActive := true
 | |
| 	switch sub.(type) {
 | |
| 	case TaskStarter, TaskBlock, TaskGo:
 | |
| 	case IJob:
 | |
| 		shouldActive = false
 | |
| 	}
 | |
| 	select {
 | |
| 	case e.getInput() <- sub:
 | |
| 		if shouldActive || mt.IsStopped() {
 | |
| 			e.active(mt)
 | |
| 		}
 | |
| 		return nil
 | |
| 	default:
 | |
| 		return ErrTooManyChildren
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (e *EventLoop) run(mt *Job) {
 | |
| 	mt.Debug("event loop start", "jobId", mt.GetTaskID(), "type", mt.GetOwnerType())
 | |
| 	ch := e.getInput()
 | |
| 	e.cases = []reflect.SelectCase{{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)}}
 | |
| 	defer func() {
 | |
| 		err := recover()
 | |
| 		if err != nil {
 | |
| 			mt.Error("job panic", "err", err, "stack", string(debug.Stack()))
 | |
| 			if !ThrowPanic {
 | |
| 				mt.Stop(errors.Join(err.(error), ErrPanic))
 | |
| 			} else {
 | |
| 				panic(err)
 | |
| 			}
 | |
| 		}
 | |
| 		mt.Debug("event loop exit", "jobId", mt.GetTaskID(), "type", mt.GetOwnerType())
 | |
| 		if !mt.handler.keepalive() {
 | |
| 			if mt.blocked != nil {
 | |
| 				mt.Stop(errors.Join(mt.blocked.StopReason(), ErrAutoStop))
 | |
| 			} else {
 | |
| 				mt.Stop(ErrAutoStop)
 | |
| 			}
 | |
| 		}
 | |
| 		mt.blocked = nil
 | |
| 	}()
 | |
| 
 | |
| 	// Main event loop - only exit when no more events AND no children
 | |
| 	for {
 | |
| 		if len(ch) == 0 && len(e.children) == 0 {
 | |
| 			if e.running.CompareAndSwap(true, false) {
 | |
| 				if len(ch) > 0 { // if add before running set to false
 | |
| 					e.active(mt)
 | |
| 				}
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 		mt.blocked = nil
 | |
| 		if chosen, rev, ok := reflect.Select(e.cases); chosen == 0 {
 | |
| 			if !ok {
 | |
| 				mt.Debug("job addSub channel closed, exiting", "taskId", mt.GetTaskID())
 | |
| 				mt.Stop(ErrAutoStop)
 | |
| 				return
 | |
| 			}
 | |
| 			switch v := rev.Interface().(type) {
 | |
| 			case func():
 | |
| 				v()
 | |
| 			case ITask:
 | |
| 				if len(e.cases) >= 65535 {
 | |
| 					mt.Warn("task children too many, may cause performance issue", "count", len(e.cases), "taskId", mt.GetTaskID(), "taskType", mt.GetTaskType(), "ownerType", mt.GetOwnerType())
 | |
| 					v.Stop(ErrTooManyChildren)
 | |
| 					continue
 | |
| 				}
 | |
| 				if mt.blocked = v; v.start() {
 | |
| 					e.cases = append(e.cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(v.GetSignal())})
 | |
| 					e.children = append(e.children, v)
 | |
| 					mt.onChildStart(v)
 | |
| 				} else {
 | |
| 					mt.removeChild(v)
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			taskIndex := chosen - 1
 | |
| 			child := e.children[taskIndex]
 | |
| 			mt.blocked = child
 | |
| 			switch tt := mt.blocked.(type) {
 | |
| 			case IChannelTask:
 | |
| 				if tt.IsStopped() {
 | |
| 					switch ttt := tt.(type) {
 | |
| 					case ITickTask:
 | |
| 						ttt.GetTicker().Stop()
 | |
| 					}
 | |
| 					mt.onChildDispose(child)
 | |
| 					mt.removeChild(child)
 | |
| 					e.children = slices.Delete(e.children, taskIndex, taskIndex+1)
 | |
| 					e.cases = slices.Delete(e.cases, chosen, chosen+1)
 | |
| 				} else {
 | |
| 					tt.Tick(rev.Interface())
 | |
| 				}
 | |
| 			default:
 | |
| 				if !ok {
 | |
| 					if mt.onChildDispose(child); child.checkRetry(child.StopReason()) {
 | |
| 						if child.reset(); child.start() {
 | |
| 							e.cases[chosen].Chan = reflect.ValueOf(child.GetSignal())
 | |
| 							mt.onChildStart(child)
 | |
| 							continue
 | |
| 						}
 | |
| 					}
 | |
| 					mt.removeChild(child)
 | |
| 					e.children = slices.Delete(e.children, taskIndex, taskIndex+1)
 | |
| 					e.cases = slices.Delete(e.cases, chosen, chosen+1)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |