Esempio n. 1
0
        /*
         * //----------------------------------------------------------------------------
         * // 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();
            }
        }
Esempio n. 2
0
        /// <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);
        }