github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/aeron/aeron.go (about)

     1  // Copyright 2016 Stanislav Liberman
     2  // Copyright 2022 Talos, Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  // http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package aeron
    17  
    18  import (
    19  	"github.com/lirm/aeron-go/aeron/atomic"
    20  	"time"
    21  
    22  	"github.com/lirm/aeron-go/aeron/broadcast"
    23  	"github.com/lirm/aeron-go/aeron/counters"
    24  	"github.com/lirm/aeron-go/aeron/driver"
    25  	"github.com/lirm/aeron-go/aeron/logging"
    26  	rb "github.com/lirm/aeron-go/aeron/ringbuffer"
    27  	"github.com/lirm/aeron-go/aeron/util/memmap"
    28  )
    29  
    30  // NullValue is used to represent a null value for when some value is not yet set.
    31  const NullValue = -1
    32  
    33  // NewPublicationHandler is the handler type for new publication notification from the media driver
    34  type NewPublicationHandler func(string, int32, int32, int64)
    35  
    36  // NewSubscriptionHandler is the handler type for new subscription notification from the media driver
    37  type NewSubscriptionHandler func(string, int32, int64)
    38  
    39  // AvailableImageHandler is the handler type for image available notification from the media driver
    40  type AvailableImageHandler func(Image)
    41  
    42  // UnavailableImageHandler is the handler type for image unavailable notification from the media driver
    43  type UnavailableImageHandler func(Image)
    44  
    45  // AvailableCounterHandler is the function called by Aeron to deliver notification of a Counter being available.
    46  // Implementations should do the minimum work for passing off state to another thread for later processing and should
    47  // not make a reentrant call back into the Aeron instance.  Note that this is an interface instead of a function in
    48  // order to support RemoveAvailableCounterHandler.
    49  type AvailableCounterHandler interface {
    50  	Handle(countersReader *counters.Reader, registrationId int64, counterId int32)
    51  }
    52  
    53  // UnavailableCounterHandler is for notification of Counters being removed via an Aeron client.  Note that this is an
    54  // interface instead of a function in order to support RemoveAvailableCounterHandler.
    55  type UnavailableCounterHandler interface {
    56  	Handle(countersReader *counters.Reader, registrationId int64, counterId int32)
    57  }
    58  
    59  // Aeron is the primary interface to the media driver for managing subscriptions and publications
    60  type Aeron struct {
    61  	context            *Context
    62  	conductor          ClientConductor
    63  	toDriverRingBuffer rb.ManyToOne
    64  	driverProxy        driver.Proxy
    65  
    66  	counters *counters.MetaDataFlyweight
    67  	cncFile  *memmap.File
    68  
    69  	toClientsBroadcastReceiver *broadcast.Receiver
    70  	toClientsCopyReceiver      *broadcast.CopyReceiver
    71  }
    72  
    73  var logger = logging.MustGetLogger("aeron")
    74  
    75  // Connect is the factory method used to create a new instance of Aeron based on Context settings
    76  func Connect(ctx *Context) (*Aeron, error) {
    77  	aeron := new(Aeron)
    78  	aeron.context = ctx
    79  	logger.Debugf("Connecting with context: %v", ctx)
    80  
    81  	ctr, cnc, err := counters.MapFile(ctx.CncFileName())
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	aeron.counters = ctr
    86  	aeron.cncFile = cnc
    87  
    88  	aeron.toDriverRingBuffer.Init(aeron.counters.ToDriverBuf.Get())
    89  
    90  	aeron.driverProxy.Init(&aeron.toDriverRingBuffer)
    91  
    92  	aeron.toClientsBroadcastReceiver, err = broadcast.NewReceiver(aeron.counters.ToClientsBuf.Get())
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	aeron.toClientsCopyReceiver = broadcast.NewCopyReceiver(aeron.toClientsBroadcastReceiver)
    98  
    99  	clientLivenessTo := time.Duration(aeron.counters.ClientLivenessTo.Get())
   100  
   101  	aeron.conductor.Init(&aeron.driverProxy, aeron.toClientsCopyReceiver, clientLivenessTo, ctx.mediaDriverTo,
   102  		ctx.publicationConnectionTo, ctx.resourceLingerTo, aeron.counters)
   103  
   104  	aeron.conductor.onAvailableImageHandler = ctx.availableImageHandler
   105  	aeron.conductor.onUnavailableImageHandler = ctx.unavailableImageHandler
   106  	aeron.conductor.onNewPublicationHandler = ctx.newPublicationHandler
   107  	aeron.conductor.onNewSubscriptionHandler = ctx.newSubscriptionHandler
   108  
   109  	if ctx.availableCounterHandler != nil {
   110  		aeron.conductor.AddAvailableCounterHandler(ctx.availableCounterHandler)
   111  	}
   112  	if ctx.unavailableCounterHandler != nil {
   113  		aeron.conductor.AddUnavailableCounterHandler(ctx.unavailableCounterHandler)
   114  	}
   115  
   116  	aeron.conductor.errorHandler = ctx.errorHandler
   117  
   118  	aeron.conductor.Start(ctx.idleStrategy)
   119  
   120  	return aeron, nil
   121  }
   122  
   123  // Close will terminate client conductor and remove all publications and subscriptions from the media driver
   124  func (aeron *Aeron) Close() error {
   125  	err := aeron.conductor.Close()
   126  	if nil != err {
   127  		aeron.context.errorHandler(err)
   128  	}
   129  
   130  	err = aeron.cncFile.Close()
   131  	if nil != err {
   132  		aeron.context.errorHandler(err)
   133  	}
   134  
   135  	return err
   136  }
   137  
   138  // AddSubscription will add a new subscription to the driver and wait until it is ready.
   139  func (aeron *Aeron) AddSubscription(channel string, streamID int32) (*Subscription, error) {
   140  	return aeron.AddSubscriptionWithHandlers(channel, streamID,
   141  		aeron.context.availableImageHandler, aeron.context.unavailableImageHandler)
   142  }
   143  
   144  // AddSubscriptionWithHandlers will add a new subscription to the driver and wait until it is ready.  It will use the
   145  // specified Handlers for available/unavailable Images instead of the default handlers.
   146  func (aeron *Aeron) AddSubscriptionWithHandlers(channel string, streamID int32,
   147  	onAvailableImage AvailableImageHandler, onUnavailableImage UnavailableImageHandler) (*Subscription, error) {
   148  	registrationID, err :=
   149  		aeron.conductor.AddSubscriptionWithHandlers(channel, streamID, onAvailableImage, onUnavailableImage)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	for {
   154  		subscription, err := aeron.conductor.FindSubscription(registrationID)
   155  		if subscription != nil || err != nil {
   156  			return subscription, err
   157  		}
   158  		aeron.context.idleStrategy.Idle(0)
   159  	}
   160  }
   161  
   162  // AddSubscriptionDeprecated will add a new subscription to the driver.
   163  // Returns a channel, which can be used for either blocking or non-blocking want for media driver confirmation
   164  func (aeron *Aeron) AddSubscriptionDeprecated(channel string, streamID int32) chan *Subscription {
   165  	ch := make(chan *Subscription, 1)
   166  	registrationID, err := aeron.conductor.AddSubscription(channel, streamID)
   167  	if err != nil {
   168  		// Preserve the legacy functionality.  The original AddSubscription would result in the ClientConductor calling
   169  		// onError on this, as well as subsequently from the FindSubscription call below.
   170  		aeron.conductor.onError(err)
   171  	}
   172  	go func() {
   173  		for {
   174  			subscription, err := aeron.conductor.FindSubscription(registrationID)
   175  			if subscription != nil || err != nil {
   176  				if err != nil {
   177  					aeron.conductor.onError(err)
   178  				}
   179  				ch <- subscription
   180  				close(ch)
   181  				return
   182  			}
   183  			aeron.context.idleStrategy.Idle(0)
   184  		}
   185  	}()
   186  	return ch
   187  }
   188  
   189  // AsyncAddSubscription will add a new subscription to the driver and return its registration ID.  That ID can be used
   190  // to get the Subscription with GetSubscription().
   191  func (aeron *Aeron) AsyncAddSubscription(channel string, streamID int32) (int64, error) {
   192  	return aeron.conductor.AddSubscriptionWithHandlers(channel, streamID,
   193  		aeron.context.availableImageHandler, aeron.context.unavailableImageHandler)
   194  }
   195  
   196  // AsyncAddSubscriptionWithHandlers will add a new subscription to the driver and return its registration ID.  That ID
   197  // can be used to get the Subscription with GetSubscription().  This call will use the specified Handlers for
   198  // available/unavailable Images instead of the default handlers.
   199  func (aeron *Aeron) AsyncAddSubscriptionWithHandlers(channel string, streamID int32,
   200  	onAvailableImage AvailableImageHandler, onUnavailableImage UnavailableImageHandler) (int64, error) {
   201  	return aeron.conductor.AddSubscriptionWithHandlers(channel, streamID, onAvailableImage, onUnavailableImage)
   202  }
   203  
   204  // GetSubscription will attempt to get a Subscription from a registrationID.  See AsyncAddSubscription.  A pending
   205  // Subscription will return nil,nil signifying that there is neither a Subscription nor an error.
   206  func (aeron *Aeron) GetSubscription(registrationID int64) (*Subscription, error) {
   207  	return aeron.conductor.FindSubscription(registrationID)
   208  }
   209  
   210  // AddPublicationDeprecated will add a new publication to the driver. If such publication already exists within ClientConductor
   211  // the same instance will be returned.
   212  // Returns a channel, which can be used for either blocking or non-blocking want for media driver confirmation
   213  func (aeron *Aeron) AddPublicationDeprecated(channel string, streamID int32) chan *Publication {
   214  	ch := make(chan *Publication, 1)
   215  
   216  	registrationID, err := aeron.conductor.AddPublication(channel, streamID)
   217  	if err != nil {
   218  		// Preserve the legacy functionality.  The original AddPublication would result in the ClientConductor calling
   219  		// onError on this, as well as subsequently from the FindPublication call below.
   220  		aeron.conductor.onError(err)
   221  	}
   222  	go func() {
   223  		for {
   224  			publication, err := aeron.conductor.FindPublication(registrationID)
   225  			if publication != nil || err != nil {
   226  				if err != nil {
   227  					aeron.conductor.onError(err)
   228  				}
   229  				ch <- publication
   230  				close(ch)
   231  				return
   232  			}
   233  			aeron.context.idleStrategy.Idle(0)
   234  		}
   235  	}()
   236  
   237  	return ch
   238  }
   239  
   240  // AddPublication will add a new publication to the driver. If such publication already exists within ClientConductor
   241  // the same instance will be returned.
   242  func (aeron *Aeron) AddPublication(channel string, streamID int32) (*Publication, error) {
   243  	registrationID, err := aeron.conductor.AddPublication(channel, streamID)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  	for {
   248  		publication, err := aeron.conductor.FindPublication(registrationID)
   249  		if publication != nil || err != nil {
   250  			return publication, err
   251  		}
   252  		aeron.context.idleStrategy.Idle(0)
   253  	}
   254  }
   255  
   256  // AsyncAddPublication will add a new publication to the driver and return its registration ID.  That ID can be used to
   257  // get the added Publication with GetPublication().
   258  func (aeron *Aeron) AsyncAddPublication(channel string, streamID int32) (int64, error) {
   259  	return aeron.conductor.AddPublication(channel, streamID)
   260  }
   261  
   262  // GetPublication will attempt to get a Publication from a registrationID.  See AsyncAddPublication.  A pending
   263  // Publication will return nil,nil signifying that there is neither a Publication nor an error.
   264  func (aeron *Aeron) GetPublication(registrationID int64) (*Publication, error) {
   265  	return aeron.conductor.FindPublication(registrationID)
   266  }
   267  
   268  // AddExclusivePublication will add a new exclusive publication to the driver. If such publication already
   269  // exists within ClientConductor the same instance will be returned.
   270  func (aeron *Aeron) AddExclusivePublication(channel string, streamID int32) (*Publication, error) {
   271  	registrationID, err := aeron.conductor.AddExclusivePublication(channel, streamID)
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  	for {
   276  		publication, err := aeron.conductor.FindPublication(registrationID)
   277  		if publication != nil || err != nil {
   278  			return publication, err
   279  		}
   280  		aeron.context.idleStrategy.Idle(0)
   281  	}
   282  }
   283  
   284  // AddExclusivePublicationDeprecated will add a new exclusive publication to the driver. If such publication already
   285  // exists within ClientConductor the same instance will be returned.
   286  // Returns a channel, which can be used for either blocking or non-blocking want for media driver confirmation
   287  func (aeron *Aeron) AddExclusivePublicationDeprecated(channel string, streamID int32) chan *Publication {
   288  	ch := make(chan *Publication, 1)
   289  
   290  	registrationID, err := aeron.conductor.AddExclusivePublication(channel, streamID)
   291  	if err != nil {
   292  		// Preserve the legacy functionality.  The original AddExclusivePublication would result in the ClientConductor
   293  		// calling onError on this, as well as subsequently from the FindPublication call below.
   294  		aeron.conductor.onError(err)
   295  	}
   296  	go func() {
   297  		for {
   298  			publication, err := aeron.conductor.FindPublication(registrationID)
   299  			if publication != nil || err != nil {
   300  				if err != nil {
   301  					aeron.conductor.onError(err)
   302  				}
   303  				ch <- publication
   304  				close(ch)
   305  				return
   306  			}
   307  			aeron.context.idleStrategy.Idle(0)
   308  		}
   309  	}()
   310  
   311  	return ch
   312  }
   313  
   314  // AsyncAddExclusivePublication will add a new exclusive publication to the driver and return its registration ID.  That
   315  // ID can be used to get the added exclusive Publication with GetExclusivePublication().
   316  func (aeron *Aeron) AsyncAddExclusivePublication(channel string, streamID int32) (int64, error) {
   317  	return aeron.conductor.AddExclusivePublication(channel, streamID)
   318  }
   319  
   320  // GetExclusivePublication will attempt to get an exclusive Publication from a registrationID.  See
   321  // AsyncAddExclusivePublication.  A pending Publication will return nil,nil signifying that there is neither a
   322  // Publication nor an error.
   323  // Also note that while aeron-go currently handles GetPublication and GetExclusivePublication the same way, they may
   324  // diverge in the future.  Other Aeron languages already handle these calls differently.
   325  func (aeron *Aeron) GetExclusivePublication(registrationID int64) (*Publication, error) {
   326  	return aeron.conductor.FindPublication(registrationID)
   327  }
   328  
   329  // NextCorrelationID generates the next correlation id that is unique for the connected Media Driver.
   330  // This is useful generating correlation identifiers for pairing requests with responses in a clients own
   331  // application protocol.
   332  //
   333  // This method is thread safe and will work across processes that all use the same media driver.
   334  func (aeron *Aeron) NextCorrelationID() int64 {
   335  	return aeron.driverProxy.NextCorrelationID()
   336  }
   337  
   338  // ClientID returns the client identity that has been allocated for communicating with the media driver.
   339  func (aeron *Aeron) ClientID() int64 {
   340  	return aeron.driverProxy.ClientID()
   341  }
   342  
   343  // CounterReader returns Aeron's clientconductor's counterReader
   344  func (aeron *Aeron) CounterReader() *counters.Reader {
   345  	return aeron.conductor.CounterReader()
   346  }
   347  
   348  // AddCounter allocates a counter on the media driver and returns its registrationId.  The Counter should be freed by
   349  // calling Counter.Close().
   350  func (aeron *Aeron) AddCounter(
   351  	typeId int32,
   352  	keyBuffer *atomic.Buffer,
   353  	keyOffset int32,
   354  	keyLength int32,
   355  	labelBuffer *atomic.Buffer,
   356  	labelOffset int32,
   357  	labelLength int32) (int64, error) {
   358  	return aeron.conductor.AddCounter(typeId, keyBuffer, keyOffset, keyLength, labelBuffer, labelOffset, labelLength)
   359  }
   360  
   361  // AddCounterByLabel allocates a counter on the media driver and returns its registrationId.  The Counter should be
   362  // freed by calling Counter.Close().
   363  func (aeron *Aeron) AddCounterByLabel(typeId int32, label string) (int64, error) {
   364  	return aeron.conductor.AddCounterByLabel(typeId, label)
   365  }
   366  
   367  // FindCounter retrieves the Counter associated with the given registrationID.  This function is non-blocking.  The
   368  // value returned is dependent on what has occurred with respect to the media driver:
   369  //
   370  // - If the registrationID is unknown, an error is returned.
   371  // - If the media driver has not answered the command, (nil,nil) is returned.
   372  // - If the media driver has successfully added the Counter then what is returned is the Counter.
   373  // - If the media driver has returned an error, that error will be returned.
   374  func (aeron *Aeron) FindCounter(registrationID int64) (*Counter, error) {
   375  	return aeron.conductor.FindCounter(registrationID)
   376  }
   377  
   378  // AddAvailableCounterHandler adds a handler to the list to be called when a counter becomes available.  Return the
   379  // registrationID to use to remove the handler.
   380  func (aeron *Aeron) AddAvailableCounterHandler(handler AvailableCounterHandler) int64 {
   381  	return aeron.conductor.AddAvailableCounterHandler(handler)
   382  }
   383  
   384  // RemoveAvailableCounterHandlerById removes a previously added handler from the list to be called when Counters become
   385  // available.  Returns true iff the handler was found and removed.
   386  func (aeron *Aeron) RemoveAvailableCounterHandlerById(registrationId int64) bool {
   387  	return aeron.conductor.RemoveAvailableCounterHandlerById(registrationId)
   388  }
   389  
   390  // RemoveAvailableCounterHandler removes a previously added handler from the list to be called when Counters become
   391  // available.  Returns true iff the handler was found and removed.
   392  func (aeron *Aeron) RemoveAvailableCounterHandler(handler AvailableCounterHandler) bool {
   393  	return aeron.conductor.RemoveAvailableCounterHandler(handler)
   394  }
   395  
   396  // AddUnavailableCounterHandler adds a handler to the list to be called when Counters become unavailable.  Return the
   397  // registrationID to use to remove the handler.
   398  func (aeron *Aeron) AddUnavailableCounterHandler(handler UnavailableCounterHandler) int64 {
   399  	return aeron.conductor.AddUnavailableCounterHandler(handler)
   400  }
   401  
   402  // RemoveUnavailableCounterHandlerById removes a previously added handler from the list to be called when Counters
   403  // become unavailable.  Returns true iff the handler was found and removed.
   404  func (aeron *Aeron) RemoveUnavailableCounterHandlerById(registrationId int64) bool {
   405  	return aeron.conductor.RemoveUnavailableCounterHandlerById(registrationId)
   406  }
   407  
   408  // RemoveUnavailableCounterHandler removes a previously added handler from the list to be called when Counters become
   409  // unavailable.  Returns true iff the handler was found and removed.
   410  func (aeron *Aeron) RemoveUnavailableCounterHandler(handler UnavailableCounterHandler) bool {
   411  	return aeron.conductor.RemoveUnavailableCounterHandler(handler)
   412  }
   413  
   414  // IsClosed returns true if this connection is closed.
   415  func (aeron *Aeron) IsClosed() bool {
   416  	return !aeron.conductor.running.Get()
   417  }