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  }