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  }