/// <summary> /// Allows writing thread to write next message. /// Can be called simultaneously from multiple threads. /// </summary> /// <returns> /// False if there was not enough room in the inbox to write /// the message. /// </returns> public bool TryWrite(Span <byte> toWrite) { if (!ClientServerMessage.TryGetLength(toWrite, out var messageSize)) { throw new ArgumentException("Message shorter than 4 bytes."); } // Verify the validity of the given message length. if (toWrite.Length != messageSize) { throw new ArgumentOutOfRangeException( $"Message of size {messageSize} in span of size {toWrite.Length}."); } if (toWrite.Length > ClientServerMessage.MaxSizeInBytes) { throw new ArgumentOutOfRangeException( $"Message size {messageSize} exceeds {ClientServerMessage.MaxSizeInBytes}."); } // Reserve a space to write by moving `_write` forward by the expected // length. `write` remembers the start of the reserved space. long write; do { write = _write; if (_sizeInBytes - (int)(write - _start) < toWrite.Length) { return(false); } } while (write != Interlocked.CompareExchange(ref _write, write + toWrite.Length, write)); try { // Write to the reserved space var start = write % _sizeInBytes; for (var i = 0; i < toWrite.Length; ++i) { _inbox[start + i] = toWrite[i]; } return(true); } finally { // Wait until we can move `_end` forward (previously pending writes must finish // first, or we'll expose readers to incomplete messages). while (_end <= write) { Interlocked.CompareExchange(ref _end, write + toWrite.Length, write); } } }
/// <summary> /// Read the next message of the Inbox /// without erasing the read data. /// </summary> public Span <byte> Peek() { int startMod = (int)(_start % _sizeInBytes); if (_start < _end) { if (ClientServerMessage.TryGetLength(new Span <byte>(_inbox).Slice(startMod), out var bufferSize)) { return(new Span <byte>(_inbox, startMod, bufferSize)); } } return(new Span <byte>(_inbox, startMod, 0)); }