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  }