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 }