|
- package ioswitch
-
- import (
- "context"
- "fmt"
- "sync"
-
- "gitlink.org.cn/cloudream/common/pkgs/future"
- "gitlink.org.cn/cloudream/common/utils/lo2"
- "gitlink.org.cn/cloudream/common/utils/sync2"
- )
-
- type bindingVars struct {
- Waittings []Var
- Bindeds []Var
- Callback *future.SetVoidFuture
- }
-
- type Switch struct {
- plan Plan
- vars map[VarID]Var
- bindings []*bindingVars
- lock sync.Mutex
- }
-
- func NewSwitch(plan Plan) *Switch {
- planning := Switch{
- plan: plan,
- vars: make(map[VarID]Var),
- }
-
- return &planning
- }
-
- func (s *Switch) Plan() *Plan {
- return &s.plan
- }
-
- func (s *Switch) Run(ctx context.Context) error {
- ctx2, cancel := context.WithCancel(ctx)
- defer cancel()
-
- return sync2.ParallelDo(s.plan.Ops, func(o Op, idx int) error {
- err := o.Execute(ctx2, s)
-
- s.lock.Lock()
- defer s.lock.Unlock()
-
- if err != nil {
- cancel()
- return fmt.Errorf("%T: %w", o, err)
- }
-
- return nil
- })
- }
-
- func (s *Switch) BindVars(ctx context.Context, vs ...Var) error {
- s.lock.Lock()
-
- callback := future.NewSetVoid()
- binding := &bindingVars{
- Callback: callback,
- }
-
- for _, v := range vs {
- v2 := s.vars[v.GetID()]
- if v2 == nil {
- binding.Waittings = append(binding.Waittings, v)
- continue
- }
-
- if err := AssignVar(v2, v); err != nil {
- s.lock.Unlock()
- return fmt.Errorf("assign var %v to %v: %w", v2.GetID(), v.GetID(), err)
- }
-
- binding.Bindeds = append(binding.Bindeds, v)
- }
-
- if len(binding.Waittings) == 0 {
- s.lock.Unlock()
- return nil
- }
-
- s.bindings = append(s.bindings, binding)
- s.lock.Unlock()
-
- err := callback.Wait(ctx)
-
- s.lock.Lock()
- defer s.lock.Unlock()
-
- s.bindings = lo2.Remove(s.bindings, binding)
-
- return err
- }
-
- func (s *Switch) PutVars(vs ...Var) {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- loop:
- for _, v := range vs {
- for ib, b := range s.bindings {
- for iw, w := range b.Waittings {
- if w.GetID() != v.GetID() {
- continue
- }
-
- if err := AssignVar(v, w); err != nil {
- b.Callback.SetError(fmt.Errorf("assign var %v to %v: %w", v.GetID(), w.GetID(), err))
- // 绑定类型不对,说明生成的执行计划有问题,怎么处理都可以,因为最终会执行失败
- continue loop
- }
-
- b.Bindeds = append(b.Bindeds, w)
- b.Waittings = lo2.RemoveAt(b.Waittings, iw)
- if len(b.Waittings) == 0 {
- b.Callback.SetVoid()
- s.bindings = lo2.RemoveAt(s.bindings, ib)
- }
-
- // 绑定成功,继续最外层循环
- continue loop
- }
-
- }
-
- // 如果没有绑定,则直接放入变量表中
- s.vars[v.GetID()] = v
- }
- }
-
- func BindArrayVars[T Var](sw *Switch, ctx context.Context, vs []T) error {
- var vs2 []Var
- for _, v := range vs {
- vs2 = append(vs2, v)
- }
-
- return sw.BindVars(ctx, vs2...)
- }
-
- func PutArrayVars[T Var](sw *Switch, vs []T) {
- var vs2 []Var
- for _, v := range vs {
- vs2 = append(vs2, v)
- }
-
- sw.PutVars(vs2...)
- }
|