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 }