github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/aeron/ringbuffer/manytoone.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 rb 18 19 import ( 20 "fmt" 21 22 "github.com/lirm/aeron-go/aeron/atomic" 23 "github.com/lirm/aeron-go/aeron/util" 24 ) 25 26 const insufficientCapacity int32 = -2 27 28 var descriptor = struct { 29 tailPositionOffset int32 30 headCachePositionOffset int32 31 headPositionOffset int32 32 correlationCounterOffset int32 33 consumerHeartbeatOffset int32 34 trailerLength int32 35 }{ 36 util.CacheLineLength * 2, 37 util.CacheLineLength * 4, 38 util.CacheLineLength * 6, 39 util.CacheLineLength * 8, 40 util.CacheLineLength * 10, 41 util.CacheLineLength * 12, 42 } 43 44 type ManyToOne struct { 45 buffer *atomic.Buffer 46 capacity int32 47 maxMsgLength int32 48 headPositionIndex int32 49 headCachePositionIndex int32 50 tailPositionIndex int32 51 correlationIDCounterIndex int32 52 consumerHeartbeatIndex int32 53 } 54 55 // Init is the main initialization method 56 func (buf *ManyToOne) Init(buffer *atomic.Buffer) *ManyToOne { 57 58 buf.buffer = buffer 59 buf.capacity = buffer.Capacity() - descriptor.trailerLength 60 61 if buf.capacity <= 0 { 62 panic("Undelying buffer capacity is infufficient") 63 } 64 util.IsPowerOfTwo(int64(buf.capacity)) 65 66 buf.maxMsgLength = buf.capacity / 8 67 buf.tailPositionIndex = buf.capacity + descriptor.tailPositionOffset 68 buf.headCachePositionIndex = buf.capacity + descriptor.headCachePositionOffset 69 buf.headPositionIndex = buf.capacity + descriptor.headPositionOffset 70 buf.correlationIDCounterIndex = buf.capacity + descriptor.correlationCounterOffset 71 buf.consumerHeartbeatIndex = buf.capacity + descriptor.consumerHeartbeatOffset 72 73 return buf 74 } 75 76 func (buf *ManyToOne) NextCorrelationID() int64 { 77 return buf.buffer.GetAndAddInt64(buf.correlationIDCounterIndex, 1) 78 } 79 80 func (buf *ManyToOne) ConsumerHeartbeatTime() int64 { 81 return buf.buffer.GetInt64Volatile(buf.consumerHeartbeatIndex) 82 } 83 84 func (buf *ManyToOne) setConsumerHeartbeatTime(time int64) { 85 buf.buffer.PutInt64Ordered(buf.consumerHeartbeatIndex, time) 86 } 87 88 func (buf *ManyToOne) producerPosition() int64 { 89 return buf.buffer.GetInt64Volatile(buf.tailPositionIndex) 90 } 91 92 func (buf *ManyToOne) consumerPosition() int64 { 93 return buf.buffer.GetInt64Volatile(buf.headPositionIndex) 94 } 95 96 func (buf *ManyToOne) claimCapacity(requiredCapacity int32) int32 { 97 98 mask := buf.capacity - 1 99 head := buf.buffer.GetInt64Volatile(buf.headCachePositionIndex) 100 101 var tail int64 102 var tailIndex int32 103 var padding int32 104 105 for ok := true; ok; ok = !buf.buffer.CompareAndSetInt64(buf.tailPositionIndex, tail, tail+int64(requiredCapacity)+int64(padding)) { 106 tail = buf.buffer.GetInt64Volatile(buf.tailPositionIndex) 107 availableCapacity := buf.capacity - int32(tail-head) 108 109 if requiredCapacity > availableCapacity { 110 head = buf.buffer.GetInt64Volatile(buf.headPositionIndex) 111 112 if requiredCapacity > (buf.capacity - int32(tail-head)) { 113 return insufficientCapacity 114 } 115 116 buf.buffer.PutInt64Ordered(buf.headCachePositionIndex, head) 117 } 118 119 padding = 0 120 tailIndex = int32(tail & int64(mask)) 121 toBufferEndLength := buf.capacity - tailIndex 122 123 if requiredCapacity > toBufferEndLength { 124 headIndex := int32(head & int64(mask)) 125 126 if requiredCapacity > headIndex { 127 head = buf.buffer.GetInt64Volatile(buf.headPositionIndex) 128 headIndex = int32(head & int64(mask)) 129 130 if requiredCapacity > headIndex { 131 return insufficientCapacity 132 } 133 134 buf.buffer.PutInt64Ordered(buf.headCachePositionIndex, head) 135 } 136 137 padding = toBufferEndLength 138 } 139 } 140 141 if 0 != padding { 142 buf.buffer.PutInt64Ordered(tailIndex, makeHeader(int32(padding), RecordDescriptor.PaddingMsgTypeID)) 143 tailIndex = 0 144 } 145 146 return tailIndex 147 } 148 149 func (buf *ManyToOne) checkMsgLength(length int32) { 150 if length > buf.maxMsgLength { 151 panic(fmt.Sprintf("encoded message exceeds maxMsgLength of %d, length=%d", buf.maxMsgLength, length)) 152 } 153 } 154 155 // Write will attempt to append the bytes from srcBuffer to this ring buffer 156 func (buf *ManyToOne) Write(msgTypeID int32, srcBuffer *atomic.Buffer, srcIndex int32, length int32) bool { 157 isSuccessful := false 158 159 checkMsgTypeID(msgTypeID) 160 buf.checkMsgLength(length) 161 162 recordLength := length + RecordDescriptor.HeaderLength 163 requiredCapacity := util.AlignInt32(recordLength, RecordDescriptor.RecordAlignment) 164 recordIndex := buf.claimCapacity(requiredCapacity) 165 166 if insufficientCapacity != recordIndex { 167 buf.buffer.PutInt64Ordered(recordIndex, makeHeader(-recordLength, msgTypeID)) 168 buf.buffer.PutBytes(EncodedMsgOffset(recordIndex), srcBuffer, srcIndex, length) 169 buf.buffer.PutInt32Ordered(LengthOffset(recordIndex), recordLength) 170 171 isSuccessful = true 172 } 173 174 return isSuccessful 175 } 176 177 func (buf *ManyToOne) read(Handler, messageCountLimit int) int32 { 178 panic("Not implemented yet") 179 }