/* * //---------------------------------------------------------------------------- * // NAME: SharedCircularBufferChannel::ReaderThreadLoop * // * // PURPOSE: * // Reader loop, receives frames and processes them. * // * // RETURNS: * // * // NOTES: * // * void SharedCircularBufferChannel::ReaderThreadLoop( * CircularBuffer& buffer, * DispatchEntry* dispatchTable, * size_t dispatchEntryCount) * { * // Receiver thread. * // * while (true) * { * if (WaitAndDispatchFrame(buffer, dispatchTable, dispatchEntryCount)) * { * continue; * } * * // The wait has been interrupted, we are done. * // * break; * } * } * * } * } */ /// <inheritdoc/> public void SendMessage <TMessage>(ref TMessage msg) where TMessage : ICodegenType { // Calculate frame size. // int frameLength = FrameHeader.TypeSize + (int)CodegenTypeExtensions.GetSerializedSize(msg); frameLength = Utils.Align(frameLength, sizeof(int)); // Acquire a write region to write the frame. // uint writeOffset = AcquireWriteRegionForFrame(ref frameLength); if (writeOffset == uint.MaxValue) { // The write has been interrupted. // return; } MlosProxy.FrameHeader frame = Frame(writeOffset); // Optimization. Store the frame length with incomplete bit. // frame.Length.Store(frameLength | 1); // Store type index and hash. // frame.CodegenTypeIndex = msg.CodegenTypeIndex(); frame.CodegenTypeHash = msg.CodegenTypeHash(); // Copy the structure to the buffer. // IntPtr payload = Payload(writeOffset); CodegenTypeExtensions.Serialize(msg, payload); // Frame is ready for the reader. // SignalFrameIsReady(frame, frameLength); // If there are readers in the waiting state, we need to notify them. // if (HasReadersInWaitingState()) { ChannelPolicy.NotifyExternalReader(); } }
/// <summary> /// Wait for the frame become available. /// </summary> /// <returns>Returns an offset to the frame buffer.</returns> /// <remarks> /// Reader function. /// If the wait has been aborted, it returns uint32_t::max. /// </remarks> internal uint WaitForFrame() { uint readPosition; TChannelSpinPolicy channelSpinPolicy = default; // Create AtomicUInt32 proxy once. // StdTypesProxy.AtomicUInt32 atomicReadPosition = Sync.ReadPosition; StdTypesProxy.AtomicUInt32 atomicReaderInWaitingStateCount = Sync.ReaderInWaitingStateCount; StdTypesProxy.AtomicBool atomicTerminateChannel = Sync.TerminateChannel; MlosProxy.FrameHeader frame = default; uint shouldWait = 0; // int spinIndex = 0; while (true) { // Wait for the frame become available. // Spin on current frame (ReadOffset). // readPosition = atomicReadPosition.Load(); uint readOffset = readPosition % Size; frame = Frame(readOffset); int frameLength = frame.Length.Load(); if (frameLength > 0) { // Writer had updated the length. // Frame is ready and available for the reader. // Advance ReadIndex to end of the frame, and allow other reads to process next frame. // uint expectedReadPosition = readPosition; uint nextReadPosition = readPosition + (uint)(frameLength & (~1)); if (atomicReadPosition.LoadRelaxed() != expectedReadPosition || atomicReadPosition.CompareExchange(nextReadPosition, expectedReadPosition) != expectedReadPosition) { // Other reader advanced ReadPosition therefore it will process the frame. // channelSpinPolicy.FailedToAcquireReadRegion(); continue; } // Current reader owns the frame. Wait untill the reader completes the write. // while ((frameLength & 1) == 1) { channelSpinPolicy.WaitForFrameCompletion(); frameLength = frame.Length.Load(); } break; } channelSpinPolicy.WaitForNewFrame(); // if ((++spinIndex & 0xff) == 0) { // No frame yet, spin if the channel is stil active. // if (atomicTerminateChannel.LoadRelaxed()) { return(uint.MaxValue); } } // Wait for the synchronization primitive. // if (shouldWait != 0) { ChannelPolicy.WaitForFrame(); atomicReaderInWaitingStateCount.FetchSub(shouldWait); shouldWait = 0; } else { // Before reader enters wait state it will increase ReaderInWaitingState count and then check if are there any messages in the channel. // shouldWait = 1; atomicReaderInWaitingStateCount.FetchAdd(shouldWait); } // If (frameLength < 0) there is active cleaning up on this frame by the writer. // The read offset had already advanced, so retry. // } // Reset InWaitingState counter. // atomicReaderInWaitingStateCount.FetchSub(shouldWait); // Reader acquired read region, frame is ready. // return(readPosition % Size); }