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