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

     1  /*
     2  Copyright 2016 Stanislav Liberman
     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  
    17  package broadcast
    18  
    19  import (
    20  	"fmt"
    21  	"github.com/lirm/aeron-go/aeron/atomic"
    22  	rb "github.com/lirm/aeron-go/aeron/ringbuffer"
    23  	"github.com/lirm/aeron-go/aeron/util"
    24  )
    25  
    26  var BufferDescriptor = struct {
    27  	tailIntentCounterOffset int32
    28  	tailCounterOffset       int32
    29  	latestCounterOffset     int32
    30  	trailerLength           int32
    31  }{
    32  	0,
    33  	util.SizeOfInt64,
    34  	util.SizeOfInt64 * 2,
    35  	util.CacheLineLength * 2,
    36  }
    37  
    38  func checkCapacity(capacity int32) error {
    39  	if util.IsPowerOfTwo(int64(capacity)) {
    40  		return nil
    41  	} else {
    42  		return fmt.Errorf("capacity must be a positive power of 2 + TRAILER_LENGTH: capacity=%d", capacity)
    43  	}
    44  }
    45  
    46  type Receiver struct {
    47  	buffer                 *atomic.Buffer
    48  	capacity               int32
    49  	mask                   int64
    50  	tailIntentCounterIndex int32
    51  	tailCounterIndex       int32
    52  	latestCounterIndex     int32
    53  
    54  	recordOffset int32
    55  	cursor       int64
    56  	nextRecord   int64
    57  
    58  	lappedCount atomic.Long
    59  }
    60  
    61  func NewReceiver(buffer *atomic.Buffer) (*Receiver, error) {
    62  	recv := new(Receiver)
    63  	recv.buffer = buffer
    64  	recv.capacity = buffer.Capacity() - BufferDescriptor.trailerLength
    65  	recv.mask = int64(recv.capacity) - 1
    66  	recv.tailIntentCounterIndex = recv.capacity + BufferDescriptor.tailIntentCounterOffset
    67  	recv.tailCounterIndex = recv.capacity + BufferDescriptor.tailCounterOffset
    68  	recv.latestCounterIndex = recv.capacity + BufferDescriptor.latestCounterOffset
    69  	recv.lappedCount.Set(0)
    70  
    71  	if err := checkCapacity(recv.capacity); err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return recv, nil
    76  }
    77  
    78  func (recv *Receiver) Validate() bool {
    79  	return recv.validate(recv.cursor)
    80  }
    81  
    82  func (recv *Receiver) validate(cursor int64) bool {
    83  	return (cursor + int64(recv.capacity)) > recv.buffer.GetInt64Volatile(recv.tailIntentCounterIndex)
    84  }
    85  
    86  func (recv *Receiver) GetLappedCount() int64 {
    87  	return recv.lappedCount.Get()
    88  }
    89  
    90  func (recv *Receiver) typeID() int32 {
    91  	return recv.buffer.GetInt32(rb.TypeOffset(recv.recordOffset))
    92  }
    93  
    94  func (recv *Receiver) offset() int32 {
    95  	return rb.EncodedMsgOffset(recv.recordOffset)
    96  }
    97  
    98  func (recv *Receiver) length() int32 {
    99  	return int32(recv.buffer.GetInt32(rb.LengthOffset(recv.recordOffset))) - rb.RecordDescriptor.HeaderLength
   100  }
   101  
   102  func (recv *Receiver) receiveNext() bool {
   103  	isAvailable := false
   104  
   105  	tail := recv.buffer.GetInt64Volatile(recv.tailCounterIndex)
   106  	cursor := recv.nextRecord
   107  
   108  	if tail > cursor {
   109  		recordOffset := int32(cursor & recv.mask)
   110  
   111  		if !recv.validate(cursor) {
   112  			recv.lappedCount.Inc()
   113  			cursor = recv.buffer.GetInt64(recv.latestCounterIndex)
   114  			recordOffset = int32(cursor & recv.mask)
   115  		}
   116  
   117  		recv.cursor = cursor
   118  		length := recv.buffer.GetInt32(rb.LengthOffset(recordOffset))
   119  		alignedLength := int64(util.AlignInt32(length, rb.RecordDescriptor.RecordAlignment))
   120  		recv.nextRecord = cursor + alignedLength
   121  
   122  		if rb.RecordDescriptor.PaddingMsgTypeID == recv.buffer.GetInt32(rb.TypeOffset(recordOffset)) {
   123  			recordOffset = 0
   124  			recv.cursor = recv.nextRecord
   125  			length := recv.buffer.GetInt32(rb.LengthOffset(recordOffset))
   126  			alignedLength := int64(util.AlignInt32(length, rb.RecordDescriptor.RecordAlignment))
   127  			recv.nextRecord += alignedLength
   128  		}
   129  
   130  		recv.recordOffset = recordOffset
   131  		isAvailable = true
   132  	}
   133  
   134  	return isAvailable
   135  }