// ================================= /// <summary> /// Writes items from <see cref="_writeBuffer"/> to disk /// </summary> private void SaveWriteBufferToDisk() { Debug.Assert(_writeBuffer != null); Debug.Assert(_maxWriteBufferSize > 0); lock (_writeLock) { if (_isDisposed) { throw new ObjectDisposedException(this.GetType().Name); } // Nothing to do check if (_writeBuffer.IsEmpty || _writeBufferSize < _maxWriteBufferSize) { return; } RegionBinaryWriter writer = GetMemoryWriteStream(_maxWriteBufferSize); Debug.Assert(writer.BaseStream.Length == 0); Debug.Assert(writer.BaseStream.InnerStream.Length == 0); int itemCount = 0; T item = default(T); while (itemCount < _maxWriteBufferSize && _writeBuffer.TryDequeue(out item)) { // write item to mem stream SerializeItemToStream(item, writer); itemCount++; } if (itemCount > 0) { // Reduce counter safely int curWriteBufferSize = Interlocked.Add(ref _writeBufferSize, -itemCount); Debug.Assert(curWriteBufferSize >= 0); Debug.Assert(curWriteBufferSize <= _writeBuffer.Count); // Write all data to disk writer.BaseStream.InnerStream.WriteTo(_writeStream); _writeStream.Flush(flushToDisk: false); } ReleaseMemoryWriteStream(writer); } }
/// <summary> /// Release taken memory stream (set it back to <see cref="_cachedMemoryWriteStream"/>) /// </summary> /// <param name="bufferingWriteStream">Buffered stream to release</param> private void ReleaseMemoryWriteStream(RegionBinaryWriter bufferingWriteStream) { Debug.Assert(bufferingWriteStream != null); Debug.Assert(Monitor.IsEntered(_writeLock)); Debug.Assert(_cachedMemoryWriteStream == null); if (bufferingWriteStream.BaseStream.InnerStream.Length <= _maxCachedMemoryWriteStreamSize) { if (bufferingWriteStream.BaseStream.InnerStream.Capacity > _maxCachedMemoryWriteStreamSize) { bufferingWriteStream.BaseStream.SetOriginLength(0, -1); bufferingWriteStream.BaseStream.InnerStream.SetLength(0); bufferingWriteStream.BaseStream.InnerStream.Capacity = _maxCachedMemoryWriteStreamSize; } _cachedMemoryWriteStream = bufferingWriteStream; } }
/// <summary> /// Release taken memory stream (set it back to <see cref="_cachedMemoryWriteStream"/>) /// </summary> /// <param name="bufferingWriteStream">Buffered stream to release</param> private void ReleaseMemoryWriteStream(RegionBinaryWriter bufferingWriteStream) { TurboContract.Requires(bufferingWriteStream != null, conditionString: "bufferingWriteStream != null"); TurboContract.Assert(Monitor.IsEntered(_writeLock), conditionString: "Monitor.IsEntered(_writeLock)"); TurboContract.Assert(_cachedMemoryWriteStream == null, conditionString: "_cachedMemoryWriteStream == null"); if (bufferingWriteStream.BaseStream.InnerStream.Length <= _maxCachedMemoryWriteStreamSize) { if (bufferingWriteStream.BaseStream.InnerStream.Capacity > _maxCachedMemoryWriteStreamSize) { bufferingWriteStream.BaseStream.SetOriginLength(0, -1); bufferingWriteStream.BaseStream.InnerStream.SetLength(0); bufferingWriteStream.BaseStream.InnerStream.Capacity = _maxCachedMemoryWriteStreamSize; } _cachedMemoryWriteStream = bufferingWriteStream; } }
/// <summary> /// Writes single item to disk /// </summary> /// <param name="item">Item</param> private void SaveSingleItemToDisk(T item) { lock (_writeLock) { if (_isDisposed) { throw new ObjectDisposedException(this.GetType().Name); } RegionBinaryWriter writer = GetMemoryWriteStream(1); Debug.Assert(writer.BaseStream.Length == 0); Debug.Assert(writer.BaseStream.InnerStream.Length == 0); SerializeItemToStream(item, writer); // Write all data to disk writer.BaseStream.InnerStream.WriteTo(_writeStream); _writeStream.Flush(flushToDisk: false); ReleaseMemoryWriteStream(writer); } }
/// <summary> /// Build segment record with specified item in memory stream. /// (writes length + item bytes) /// </summary> /// <param name="item">Item</param> /// <param name="writer">Writer that wraps memory stream</param> private void SerializeItemToStream(T item, RegionBinaryWriter writer) { Debug.Assert(writer != null); Debug.Assert(Monitor.IsEntered(_writeLock)); checked { var stream = writer.BaseStream; int origin = stream.InnerStreamPosition; stream.SetOriginLength(origin + 4, -1); // Offset 4 to store length later Debug.Assert(stream.Length == 0); _serializer.Serialize(writer, item); writer.Flush(); Debug.Assert(stream.Length >= 0); int length = (int)stream.Length; stream.SetOrigin(origin); // Offset back to the beggining writer.Write((int)length); // Write length stream.Seek(0, SeekOrigin.End); // Seek to the end of the stream } }
// ================= MemoryWriteStream ================== /// <summary> /// Gets cached memory stream to write /// </summary> /// <returns>BinaryWriter with stream for in-memory writing</returns> private RegionBinaryWriter GetMemoryWriteStream(int itemCount) { Debug.Assert(Monitor.IsEntered(_writeLock)); Debug.Assert(itemCount > 0); RegionBinaryWriter result = _cachedMemoryWriteStream; _cachedMemoryWriteStream = null; if (result == null) { int expectedItemSize = _serializer.ExpectedSizeInBytes; if (expectedItemSize <= 0) { expectedItemSize = InitialCacheSizePerItem; } result = new RegionBinaryWriter(Math.Min(MaxCachedMemoryStreamSize, Math.Max(0, itemCount * (expectedItemSize + ItemHeaderSize)))); // Max used to protect from overflow } else { result.BaseStream.SetOriginLength(0, -1); result.BaseStream.SetLength(0); } return(result); }
/// <summary> /// Build segment record with specified item in memory stream. /// (writes length + item bytes) /// </summary> /// <param name="item">Item</param> /// <param name="writer">Writer that wraps memory stream</param> private void SerializeItemToStream(T item, RegionBinaryWriter writer) { TurboContract.Requires(writer != null, conditionString: "writer != null"); TurboContract.Assert(Monitor.IsEntered(_writeLock), conditionString: "Monitor.IsEntered(_writeLock)"); checked { var stream = writer.BaseStream; int origin = stream.InnerStreamPosition; stream.SetOriginLength(origin + 4, -1); // Offset 4 to store length later TurboContract.Assert(stream.Length == 0, conditionString: "stream.Length == 0"); _serializer.Serialize(writer, item); writer.Flush(); TurboContract.Assert(stream.Length >= 0, conditionString: "stream.Length >= 0"); int length = (int)stream.Length; stream.SetOrigin(origin); // Offset back to the beggining writer.Write((int)length); // Write length stream.Seek(0, SeekOrigin.End); // Seek to the end of the stream } }
/// <summary> /// NonPersistentDiskQueueSegment constructor /// </summary> /// <param name="segmentNumber">Segment number</param> /// <param name="fileName">Full file name for the segment</param> /// <param name="serializer">Items serializing/deserializing logic</param> /// <param name="capacity">Maximum number of stored items inside the segement (overall capacity)</param> /// <param name="writeBufferSize">Determines the number of items, that are stored in memory before save them to disk (-1 - set to default value, 0 - disable write buffer)</param> /// <param name="cachedMemoryWriteStreamSize">Maximum size of the cached byte stream that used to serialize items in memory (-1 - set to default value, 0 - disable byte stream caching)</param> /// <param name="readBufferSize">Determines the number of items, that are stored in memory for read purposes (-1 - set to default value, 0 - disable read buffer)</param> /// <param name="cachedMemoryReadStreamSize">Maximum size of the cached byte stream that used to deserialize items in memory (-1 - set to default value, 0 - disable byte stream caching)</param> public NonPersistentDiskQueueSegment(long segmentNumber, string fileName, IDiskQueueItemSerializer <T> serializer, int capacity, int writeBufferSize, int cachedMemoryWriteStreamSize, int readBufferSize, int cachedMemoryReadStreamSize) : base(segmentNumber, capacity, 0, 0) { if (string.IsNullOrEmpty(fileName)) { throw new ArgumentNullException(nameof(fileName)); } if (serializer == null) { throw new ArgumentNullException(nameof(serializer)); } if (File.Exists(fileName)) { throw new ArgumentException($"Can't create NonPersistentDiskQueueSegment on existing file '{fileName}'", nameof(fileName)); } _fileName = fileName; _serializer = serializer; try { _writeStream = new FileStream(_fileName, FileMode.CreateNew, FileAccess.Write, FileShare.Read); _readStream = new FileStream(_fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); _writeLock = new object(); _readLock = new object(); _writeBufferSize = 0; _maxWriteBufferSize = writeBufferSize >= 0 ? writeBufferSize : DefaultWriteBufferSize; if (writeBufferSize < 0 && serializer.ExpectedSizeInBytes > 0) { _maxWriteBufferSize = checked (Math.Max(DefaultWriteBufferSize, 4096 / Math.Min(serializer.ExpectedSizeInBytes + ItemHeaderSize, MaxChacheSizePerItem))); } if (_maxWriteBufferSize > 0) { _writeBuffer = new ConcurrentQueue <T>(); } _cachedMemoryWriteStream = null; _maxCachedMemoryWriteStreamSize = cachedMemoryWriteStreamSize; if (cachedMemoryWriteStreamSize < 0) { checked { int expectedItemSize = MaxChacheSizePerItem; if (serializer.ExpectedSizeInBytes > 0) { expectedItemSize = Math.Min(serializer.ExpectedSizeInBytes + ItemHeaderSize, MaxChacheSizePerItem); } _maxCachedMemoryWriteStreamSize = Math.Min(MaxCachedMemoryStreamSize, (_maxWriteBufferSize + 1) * expectedItemSize); } } _maxReadBufferSize = readBufferSize >= 0 ? readBufferSize : Math.Max(_writeBufferSize / 2, DefaultReadBufferSize); if (_maxReadBufferSize > 0) { _readBuffer = new ConcurrentQueue <T>(); } _cachedMemoryReadStream = null; _maxCachedMemoryReadStreamSize = cachedMemoryReadStreamSize; if (cachedMemoryReadStreamSize < 0) { checked { int expectedItemSize = MaxChacheSizePerItem; if (serializer.ExpectedSizeInBytes > 0) { expectedItemSize = Math.Min(serializer.ExpectedSizeInBytes + ItemHeaderSize, MaxChacheSizePerItem); } _maxCachedMemoryReadStreamSize = Math.Min(MaxCachedMemoryStreamSize, expectedItemSize); } } // Prepare file header WriteSegmentHeader(_writeStream, Number, Capacity); _readStream.Seek(0, SeekOrigin.End); // Offset readStream after the header _isDisposed = false; Debug.Assert(_maxWriteBufferSize >= 0); Debug.Assert(_maxCachedMemoryWriteStreamSize >= 0); Debug.Assert(_maxReadBufferSize >= 0); Debug.Assert(_maxCachedMemoryReadStreamSize >= 0); Debug.Assert(_writeStream.Position == _readStream.Position); } catch { // Should close streams to make created files available to delete if (_writeStream != null) { _writeStream.Dispose(); } if (_readStream != null) { _readStream.Dispose(); } throw; } }