github.com/benz9527/toy-box/algo@v0.0.0-20240221120937-66c0c6bd5abd/pubsub/x_sp_disruptor.go (about) 1 //go:build linux && !386 2 3 package pubsub 4 5 import ( 6 "fmt" 7 "github.com/benz9527/toy-box/algo/bit" 8 "github.com/benz9527/toy-box/algo/queue" 9 "sync/atomic" 10 "time" 11 ) 12 13 type disruptorStatus int32 14 15 const ( 16 disruptorReady disruptorStatus = iota 17 disruptorRunning 18 ) 19 20 type xSinglePipelineDisruptor[T any] struct { 21 pub interface { 22 Publisher[T] 23 stopper 24 } 25 sub interface { 26 Subscriber[T] 27 stopper 28 } 29 status disruptorStatus 30 } 31 32 func NewXSinglePipelineDisruptor[T any]( 33 capacity uint64, 34 strategy BlockStrategy, 35 handler EventHandler[T], 36 ) Disruptor[T] { 37 capacity = bit.RoundupPowOf2ByCeil(capacity) 38 seq := NewXSequencer(capacity) 39 // Can't start from 0, because 0 will be treated as nil value 40 seq.GetWriteCursor().Next() 41 seq.GetReadCursor().Next() 42 rb := queue.NewXRingBuffer[T](capacity) 43 pub := newXSinglePipelinePublisher[T](seq, rb, strategy) 44 sub := newXSinglePipelineSubscriber[T](rb, handler, seq, strategy) 45 d := &xSinglePipelineDisruptor[T]{ 46 pub: pub, 47 sub: sub, 48 status: disruptorReady, 49 } 50 return d 51 } 52 53 func (dis *xSinglePipelineDisruptor[T]) Start() error { 54 if atomic.CompareAndSwapInt32((*int32)(&dis.status), int32(disruptorReady), int32(disruptorRunning)) { 55 if err := dis.sub.Start(); err != nil { 56 atomic.StoreInt32((*int32)(&dis.status), int32(disruptorReady)) 57 return err 58 } 59 if err := dis.pub.Start(); err != nil { 60 atomic.StoreInt32((*int32)(&dis.status), int32(disruptorReady)) 61 return err 62 } 63 return nil 64 } 65 return fmt.Errorf("disruptor already started") 66 } 67 68 func (dis *xSinglePipelineDisruptor[T]) Stop() error { 69 if atomic.CompareAndSwapInt32((*int32)(&dis.status), int32(disruptorRunning), int32(disruptorReady)) { 70 if err := dis.pub.Stop(); err != nil { 71 atomic.CompareAndSwapInt32((*int32)(&dis.status), int32(disruptorRunning), int32(disruptorReady)) 72 return err 73 } 74 if err := dis.sub.Stop(); err != nil { 75 atomic.CompareAndSwapInt32((*int32)(&dis.status), int32(disruptorRunning), int32(disruptorReady)) 76 return err 77 } 78 return nil 79 } 80 return fmt.Errorf("disruptor already stopped") 81 } 82 83 func (dis *xSinglePipelineDisruptor[T]) IsStopped() bool { 84 return atomic.LoadInt32((*int32)(&dis.status)) != int32(disruptorRunning) 85 } 86 87 func (dis *xSinglePipelineDisruptor[T]) Publish(event T) (uint64, bool, error) { 88 return dis.pub.Publish(event) 89 } 90 91 func (dis *xSinglePipelineDisruptor[T]) PublishTimeout(event T, timeout time.Duration) { 92 dis.pub.PublishTimeout(event, timeout) 93 } 94 95 func (dis *xSinglePipelineDisruptor[T]) RegisterSubscriber(sub Subscriber[T]) error { 96 // Single pipeline disruptor only support one subscriber to consume the events. 97 // It will be registered at the construction. 98 return nil 99 }