/// <summary> /// Non-blocking write of an message to an underlying ring-buffer. /// </summary> /// <param name="msgTypeId"> type of the message encoding. </param> /// <param name="srcBuffer"> containing the encoded binary message. </param> /// <param name="srcIndex"> at which the encoded message begins. </param> /// <param name="length"> of the encoded message in bytes. </param> /// <returns> true if written to the ring-buffer, or false if insufficient space exists. </returns> /// <exception cref="ArgumentException"> if the length is greater than <seealso cref="IRingBuffer.MaxMsgLength()"/> </exception> public bool Write(int msgTypeId, IDirectBuffer srcBuffer, int srcIndex, int length) { RecordDescriptor.CheckTypeId(msgTypeId); CheckMsgLength(length); var isSuccessful = false; var buffer = _buffer; var recordLength = length + RecordDescriptor.HeaderLength; var requiredCapacity = BitUtil.Align(recordLength, RecordDescriptor.Alignment); var recordIndex = ClaimCapacity(buffer, requiredCapacity); if (InsufficientCapacity != recordIndex) { buffer.PutLongOrdered(recordIndex, RecordDescriptor.MakeHeader(-recordLength, msgTypeId)); // TODO JPW original: UnsafeAccess.UNSAFE.storeFence(); Thread.MemoryBarrier(); buffer.PutBytes(RecordDescriptor.EncodedMsgOffset(recordIndex), srcBuffer, srcIndex, length); buffer.PutIntOrdered(RecordDescriptor.LengthOffset(recordIndex), recordLength); isSuccessful = true; } return(isSuccessful); }
/// <summary> /// Read as many messages as are available from the ring buffer to up a supplied maximum. /// </summary> /// <param name="handler"> to be called for processing each message in turn. </param> /// <param name="messageCountLimit"> the number of messages will be read in a single invocation. </param> /// <returns> the number of messages that have been processed. </returns> public int Read(MessageHandler handler, int messageCountLimit) { var messagesRead = 0; var buffer = _buffer; var head = buffer.GetLong(_headPositionIndex); var bytesRead = 0; var capacity = _capacity; var headIndex = (int)head & (capacity - 1); var contiguousBlockLength = capacity - headIndex; try { while ((bytesRead < contiguousBlockLength) && (messagesRead < messageCountLimit)) { var recordIndex = headIndex + bytesRead; var header = buffer.GetLongVolatile(recordIndex); var recordLength = RecordDescriptor.RecordLength(header); if (recordLength <= 0) { break; } bytesRead += BitUtil.Align(recordLength, RecordDescriptor.Alignment); var messageTypeId = RecordDescriptor.MessageTypeId(header); if (PaddingMsgTypeId == messageTypeId) { continue; } ++messagesRead; handler(messageTypeId, buffer, recordIndex + RecordDescriptor.HeaderLength, recordLength - RecordDescriptor.HeaderLength); } } finally { if (bytesRead != 0) { buffer.SetMemory(headIndex, bytesRead, 0); buffer.PutLongOrdered(_headPositionIndex, head + bytesRead); } } return(messagesRead); }
/// <summary> /// Unblock a multi-producer ring buffer where a producer has died during the act of offering. The operation will scan from /// the consumer position up to the producer position. /// /// If no action is required at the position then none will be taken. /// </summary> /// <returns> true of an unblocking action was taken otherwise false. </returns> public bool Unblock() { var buffer = _buffer; var mask = _capacity - 1; var consumerIndex = (int)(buffer.GetLongVolatile(_headPositionIndex) & mask); var producerIndex = (int)(buffer.GetLongVolatile(_tailPositionIndex) & mask); if (producerIndex == consumerIndex) { return(false); } var unblocked = false; var length = buffer.GetIntVolatile(consumerIndex); if (length < 0) { buffer.PutLongOrdered(consumerIndex, RecordDescriptor.MakeHeader(-length, PaddingMsgTypeId)); unblocked = true; } else if (0 == length) { // go from (consumerIndex to producerIndex) or (consumerIndex to capacity) var limit = producerIndex > consumerIndex ? producerIndex : _capacity; var i = consumerIndex + RecordDescriptor.Alignment; do { // read the top int of every long (looking for length aligned to 8=ALIGNMENT) length = buffer.GetIntVolatile(i); if (0 != length) { if (ScanBackToConfirmStillZeroed(buffer, i, consumerIndex)) { buffer.PutLongOrdered(consumerIndex, RecordDescriptor.MakeHeader(i - consumerIndex, PaddingMsgTypeId)); unblocked = true; } break; } i += RecordDescriptor.Alignment; } while (i < limit); } return(unblocked); }
private int ClaimCapacity(IAtomicBuffer buffer, int requiredCapacity) { var capacity = _capacity; var tailPositionIndex = _tailPositionIndex; var headCachePositionIndex = _headCachePositionIndex; var mask = capacity - 1; var head = buffer.GetLongVolatile(headCachePositionIndex); long tail; int tailIndex; int padding; do { tail = buffer.GetLongVolatile(tailPositionIndex); var availableCapacity = capacity - (int)(tail - head); if (requiredCapacity > availableCapacity) { head = buffer.GetLongVolatile(_headPositionIndex); if (requiredCapacity > (capacity - (int)(tail - head))) { return(InsufficientCapacity); } buffer.PutLongOrdered(headCachePositionIndex, head); } padding = 0; tailIndex = (int)tail & mask; var toBufferEndLength = capacity - tailIndex; if (requiredCapacity > toBufferEndLength) { var headIndex = (int)head & mask; if (requiredCapacity > headIndex) { head = buffer.GetLongVolatile(_headPositionIndex); headIndex = (int)head & mask; if (requiredCapacity > headIndex) { return(InsufficientCapacity); } buffer.PutLongOrdered(headCachePositionIndex, head); } padding = toBufferEndLength; } } while (!buffer.CompareAndSetLong(tailPositionIndex, tail, tail + requiredCapacity + padding)); if (0 != padding) { buffer.PutLongOrdered(tailIndex, RecordDescriptor.MakeHeader(padding, PaddingMsgTypeId)); tailIndex = 0; } return(tailIndex); }