// nolint:unused package v2 import ( "fmt" "sync/atomic" "github.com/tendermint/tendermint/libs/log" ) type scFull struct{} type pcFull struct{} const demuxerBufferSize = 10 type demuxer struct { input chan Event scheduler *Routine processor *Routine fin chan error stopped chan struct{} rdy chan struct{} running *uint32 stopping *uint32 logger log.Logger } func newDemuxer(scheduler *Routine, processor *Routine) *demuxer { return &demuxer{ input: make(chan Event, demuxerBufferSize), scheduler: scheduler, processor: processor, stopped: make(chan struct{}, 1), fin: make(chan error, 1), rdy: make(chan struct{}, 1), running: new(uint32), stopping: new(uint32), logger: log.NewNopLogger(), } } func (dm *demuxer) setLogger(logger log.Logger) { dm.logger = logger } func (dm *demuxer) start() { starting := atomic.CompareAndSwapUint32(dm.running, uint32(0), uint32(1)) if !starting { panic("Routine has already started") } dm.logger.Info("demuxer: run") dm.rdy <- struct{}{} for { if !dm.isRunning() { break } select { case event, ok := <-dm.input: if !ok { dm.logger.Info("demuxer: stopping") dm.terminate(fmt.Errorf("stopped")) dm.stopped <- struct{}{} return } oEvents, err := dm.handle(event) if err != nil { dm.terminate(err) return } for _, event := range oEvents { dm.input <- event } case event, ok := <-dm.scheduler.next(): if !ok { dm.logger.Info("demuxer: scheduler output closed") continue } oEvents, err := dm.handle(event) if err != nil { dm.terminate(err) return } for _, event := range oEvents { dm.input <- event } case event, ok := <-dm.processor.next(): if !ok { dm.logger.Info("demuxer: processor output closed") continue } oEvents, err := dm.handle(event) if err != nil { dm.terminate(err) return } for _, event := range oEvents { dm.input <- event } } } } func (dm *demuxer) handle(event Event) (Events, error) { received := dm.scheduler.trySend(event) if !received { return Events{scFull{}}, nil // backpressure } received = dm.processor.trySend(event) if !received { return Events{pcFull{}}, nil // backpressure } return Events{}, nil } func (dm *demuxer) trySend(event Event) bool { if !dm.isRunning() || dm.isStopping() { dm.logger.Info("dummuxer isn't running") return false } select { case dm.input <- event: return true default: dm.logger.Info("demuxer channel was full") return false } } func (dm *demuxer) isRunning() bool { return atomic.LoadUint32(dm.running) == 1 } func (dm *demuxer) isStopping() bool { return atomic.LoadUint32(dm.stopping) == 1 } func (dm *demuxer) ready() chan struct{} { return dm.rdy } func (dm *demuxer) stop() { if !dm.isRunning() { return } stopping := atomic.CompareAndSwapUint32(dm.stopping, uint32(0), uint32(1)) if !stopping { panic("Demuxer has already stopped") } dm.logger.Info("demuxer stop") close(dm.input) <-dm.stopped } func (dm *demuxer) terminate(reason error) { stopped := atomic.CompareAndSwapUint32(dm.running, uint32(1), uint32(0)) if !stopped { panic("called terminate but already terminated") } dm.fin <- reason } func (dm *demuxer) final() chan error { return dm.fin }