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  }