github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/aeron/counters/reader.go (about) 1 /* 2 Copyright 2016-2018 Stanislav Liberman 3 Copyright 2022 Talos, Inc. 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 */ 17 18 // See ~agrona/agrona/src/main/java/org/agrona/concurrent/status/CountersReader.java 19 // 20 // Values Buffer 21 // 22 // 0 1 2 3 23 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 24 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 25 // | Counter Value | 26 // | | 27 // +---------------------------------------------------------------+ 28 // | Registration Id | 29 // | | 30 // +---------------------------------------------------------------+ 31 // | Owner Id | 32 // | | 33 // +---------------------------------------------------------------+ 34 // | 104 bytes of padding ... 35 // ... | 36 // +---------------------------------------------------------------+ 37 // | Repeats to end of buffer ... 38 // | | 39 // ... | 40 // +---------------------------------------------------------------+ 41 // 42 // Meta Data Buffer 43 // 44 // 0 1 2 3 45 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 46 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 47 // | Record State | 48 // +---------------------------------------------------------------+ 49 // | Type Id | 50 // +---------------------------------------------------------------+ 51 // | Free-for-reuse Deadline (ms) | 52 // | | 53 // +---------------------------------------------------------------+ 54 // | 112 bytes for key ... 55 // ... | 56 // +-+-------------------------------------------------------------+ 57 // |R| Label Length | 58 // +-+-------------------------------------------------------------+ 59 // | 380 bytes of Label ... 60 // ... | 61 // +---------------------------------------------------------------+ 62 // | Repeats to end of buffer ... 63 // | | 64 // ... | 65 // +---------------------------------------------------------------+ 66 67 package counters 68 69 import ( 70 "fmt" 71 "unsafe" 72 73 "github.com/lirm/aeron-go/aeron/atomic" 74 "github.com/lirm/aeron-go/aeron/util" 75 ) 76 77 const CounterLength = util.CacheLineLength * 2 78 79 const FullLabelLength = util.CacheLineLength * 6 80 const LabelOffset = util.CacheLineLength * 2 81 const MetadataLength = LabelOffset + FullLabelLength 82 const MaxLabelLength = FullLabelLength - util.SizeOfInt32 83 const MaxKeyLength = (util.CacheLineLength * 2) - (util.SizeOfInt32 * 2) - util.SizeOfInt64 84 85 const TypeIdOffset = util.SizeOfInt32 86 87 // FreeForReuseDeadlineOffset is the offset in the record at which the deadline (in milliseconds) for when counter may be reused. 88 const FreeForReuseDeadlineOffset = TypeIdOffset + util.SizeOfInt32 89 const KeyOffset = FreeForReuseDeadlineOffset + util.SizeOfInt64 90 91 const RecordReclaimed int32 = -1 92 const RecordUnused int32 = 0 93 const RecordAllocated int32 = 1 94 95 const NullCounterId = int32(-1) 96 97 const RegistrationIdOffset = util.SizeOfInt64 98 const OwnerIdOffset = RegistrationIdOffset + util.SizeOfInt64 99 100 type Reader struct { 101 metaData *atomic.Buffer 102 values *atomic.Buffer 103 104 maxCounterID int 105 } 106 107 type Counter struct { 108 Id int32 109 TypeId int32 110 Value int64 111 Label string 112 } 113 114 func NewReader(values, metaData *atomic.Buffer) *Reader { 115 reader := Reader{metaData: metaData, values: values} 116 reader.maxCounterID = int(values.Capacity()/CounterLength) - 1 117 118 return &reader 119 } 120 121 func counterOffset(counterId int32) int32 { 122 return counterId * CounterLength 123 } 124 125 func (reader *Reader) Scan(cb func(Counter)) { 126 127 var id int32 = 0 128 var i int32 = 0 129 130 for capacity := reader.metaData.Capacity(); i < capacity; i += MetadataLength { 131 recordStatus := reader.metaData.GetInt32Volatile(i) 132 if RecordUnused == recordStatus { 133 break 134 } else if RecordAllocated == recordStatus { 135 typeId := reader.metaData.GetInt32(i + TypeIdOffset) 136 label := reader.labelValue(i) 137 138 // TODO Get the key buffer 139 140 value := reader.values.GetInt64Volatile(counterOffset(id)) 141 142 // fmt.Printf("Reading at offset %d; counterState=%d; typeId=%d\n", i, recordStatus, typeId) 143 144 cb(Counter{id, typeId, value, label}) 145 } 146 id++ 147 } 148 } 149 150 func (reader *Reader) ScanForType(typeId int32, callback func(counterId int32, keyBuffer *atomic.Buffer) bool) { 151 var keyBuf atomic.Buffer 152 for id := 0; id < reader.maxCounterID; id++ { 153 counterId := int32(id) 154 metaDataOffset := counterId * MetadataLength 155 recordStatus := reader.metaData.GetInt32Volatile(metaDataOffset) 156 if recordStatus == RecordUnused { 157 break 158 } else if RecordAllocated == recordStatus { 159 thisTypeId := reader.metaData.GetInt32(metaDataOffset + 4) 160 if thisTypeId == typeId { 161 // requires Go 1.17: keyPtr := unsafe.Add(reader.metaData.Ptr(), metaDataOffset+KeyOffset) 162 keyPtr := unsafe.Pointer(uintptr(reader.metaData.Ptr()) + uintptr(metaDataOffset+KeyOffset)) 163 keyBuf.Wrap(keyPtr, MaxKeyLength) 164 if !callback(counterId, &keyBuf) { 165 break 166 } 167 } 168 } 169 } 170 } 171 172 func (reader *Reader) FindCounter(typeId int32, keyFilter func(keyBuffer *atomic.Buffer) bool) int32 { 173 var keyBuf atomic.Buffer 174 for id := 0; id < reader.maxCounterID; id++ { 175 metaDataOffset := int32(id) * MetadataLength 176 recordStatus := reader.metaData.GetInt32Volatile(metaDataOffset) 177 if recordStatus == RecordUnused { 178 break 179 } else if RecordAllocated == recordStatus { 180 thisTypeId := reader.metaData.GetInt32(metaDataOffset + 4) 181 if thisTypeId == typeId { 182 // requires Go 1.17: keyPtr := unsafe.Add(reader.metaData.Ptr(), metaDataOffset+KeyOffset) 183 keyPtr := unsafe.Pointer(uintptr(reader.metaData.Ptr()) + uintptr(metaDataOffset+KeyOffset)) 184 keyBuf.Wrap(keyPtr, MaxKeyLength) 185 if keyFilter == nil || keyFilter(&keyBuf) { 186 return int32(id) 187 } 188 } 189 } 190 } 191 return NullCounterId 192 } 193 194 // GetKeyPartInt32 returns an int32 portion of the key at the specified offset 195 func (reader *Reader) GetKeyPartInt32(counterId int32, offset int32) (int32, error) { 196 if err := reader.validateCounterIdAndOffset(counterId, offset+util.SizeOfInt32); err != nil { 197 return 0, err 198 } 199 metaDataOffset := counterId * MetadataLength 200 recordStatus := reader.metaData.GetInt32Volatile(metaDataOffset) 201 if recordStatus != RecordAllocated { 202 return 0, fmt.Errorf("counterId=%d recordStatus=%d", counterId, recordStatus) 203 } 204 return reader.metaData.GetInt32(metaDataOffset + KeyOffset + offset), nil 205 } 206 207 // GetKeyPartInt64 returns an int64 portion of the key at the specified offset 208 func (reader *Reader) GetKeyPartInt64(counterId int32, offset int32) (int64, error) { 209 if err := reader.validateCounterIdAndOffset(counterId, offset+util.SizeOfInt64); err != nil { 210 return 0, err 211 } 212 metaDataOffset := counterId * MetadataLength 213 recordStatus := reader.metaData.GetInt32Volatile(metaDataOffset) 214 if recordStatus != RecordAllocated { 215 return 0, fmt.Errorf("counterId=%d recordStatus=%d", counterId, recordStatus) 216 } 217 return reader.metaData.GetInt64(metaDataOffset + KeyOffset + offset), nil 218 } 219 220 // GetKeyPartString returns a string portion of the key, assuming the string is prefixed by its length 221 // (as an 32-bit int) at the specified offset. 222 func (reader *Reader) GetKeyPartString(counterId int32, offset int32) (string, error) { 223 if err := reader.validateCounterIdAndOffset(counterId, offset+util.SizeOfInt32); err != nil { 224 return "", err 225 } 226 metaDataOffset := counterId * MetadataLength 227 recordStatus := reader.metaData.GetInt32Volatile(metaDataOffset) 228 if recordStatus != RecordAllocated { 229 return "", fmt.Errorf("counterId=%d recordStatus=%d", counterId, recordStatus) 230 } 231 lengthOffset := metaDataOffset + KeyOffset + offset 232 length := reader.metaData.GetInt32(lengthOffset) 233 if length < 0 || (offset+length) > MaxKeyLength { 234 return "", fmt.Errorf("counterId=%d offset=%d length=%d", counterId, offset, length) 235 } 236 return string(reader.metaData.GetBytesArray(lengthOffset+4, length)), nil 237 } 238 239 // GetCounterValue returns the value of the given counter id (as a volatile read). 240 func (reader *Reader) GetCounterValue(counterId int32) int64 { 241 if counterId < 0 || counterId >= int32(reader.maxCounterID) { 242 return 0 243 } 244 return reader.values.GetInt64Volatile(counterOffset(counterId)) 245 } 246 247 func (reader *Reader) GetCounterRegistrationId(counterId int32) int64 { 248 if counterId < 0 || counterId >= int32(reader.maxCounterID) { 249 return 0 250 } 251 return reader.values.GetInt64Volatile(counterOffset(counterId) + RegistrationIdOffset) 252 } 253 254 func (reader *Reader) GetCounterOwnerId(counterId int32) int64 { 255 if counterId < 0 || counterId >= int32(reader.maxCounterID) { 256 return 0 257 } 258 return reader.values.GetInt64Volatile(counterOffset(counterId) + OwnerIdOffset) 259 } 260 261 // GetCounterTypeId returns the type id for a counter. 262 func (reader *Reader) GetCounterTypeId(counterId int32) int32 { 263 if counterId < 0 || counterId >= int32(reader.maxCounterID) { 264 return -1 265 } 266 return reader.metaData.GetInt32(counterId*MetadataLength + TypeIdOffset) 267 } 268 269 func (reader *Reader) IsCounterAllocated(counterId int32) bool { 270 return counterId >= 0 && counterId < int32(reader.maxCounterID) && 271 reader.metaData.GetInt32Volatile(counterId*MetadataLength) == RecordAllocated 272 } 273 274 func (reader *Reader) validateCounterIdAndOffset(counterId int32, offset int32) error { 275 if counterId < 0 || counterId >= int32(reader.maxCounterID) { 276 return fmt.Errorf("counterId=%d maxCounterId=%d", counterId, reader.maxCounterID) 277 } 278 if offset < 0 || offset >= MaxKeyLength { 279 return fmt.Errorf("counterId=%d offset=%d maxKeyLength=%d", counterId, offset, MaxKeyLength) 280 } 281 return nil 282 } 283 284 func (reader *Reader) labelValue(metaDataOffset int32) string { 285 labelSize := reader.metaData.GetInt32(metaDataOffset + LabelOffset) 286 return string(reader.metaData.GetBytesArray(metaDataOffset+LabelOffset+4, labelSize)) 287 }