/// <summary> /// Attempts to schedule a write of the specified length into the file. /// </summary> /// <returns> /// A valid block address representing the location where the written /// block will be found, or a 'None' address if the file does not /// currently have enough room for this. /// </returns> /// <remarks> /// The provided writer will be invoked at some point in the future. /// /// Thread-safe. /// </remarks> public BlockAddress TryScheduleWrite( uint realm, Hash hash, int length, WithSpan.ReadWrite writer) { if (length < 0) { throw new ArgumentException($"Negative length: {length}", nameof(length)); } int rank; long offset; lock (_syncRoot) { offset = _offset; _offset = BlockAddress.Ceiling(offset + BlockHeader.Size + length); if (_offset > _file.Length) { // Since this file is about to be flushed and closed, // because it's "full enough", we prevent additional writes. _offset = _file.Length; return(BlockAddress.None); } rank = _flags.Count; _flags.Append(ReadFlag.Triggered(() => PerformWrite(offset, writer))); } // We touched '_offset' and '_flags' in the critical section, the remaining // work on the actual byte span can be done without mutex. var span = _file.AsMemory(offset, BlockHeader.Size).Span; ref var header = ref MemoryMarshal.Cast <byte, BlockHeader>(span)[0];
/// <summary> /// Schedule the writing of a block, returns the address where the block will be /// available once written. /// </summary> /// <remarks> /// It is of course possible to immediately query the data at that address, in /// which case the data will be returned. /// /// Can be called from multiple threads. /// </remarks> public BlockAddress ScheduleWrite( uint realm, Hash hash, int length, WithSpan.ReadWrite writer) { for (var retries = 0; retries < 3; ++retries) { var currentWriter = _writer.GetCurrent(); var addr = currentWriter.TryScheduleWrite(realm, hash, length, writer); if (!addr.IsNone()) { return(addr); } // Scheduling a write failed because the current writer is full, // so close it and loop to get a new writer. _writer.RequestRecycle(currentWriter); } throw new Exception("Could not schedule write after three retries"); }
/// <summary> /// Write a block to the scratch space. It will be possible to read it /// back from the scratch space using the (realm, hash) that was /// provided to create it. /// </summary> /// <remarks> /// <paramref name="hash"/> should be the hash of the data passed in, /// according to <see cref="BlockHasher"/>. /// /// <paramref name="writer"/> will be called at some point in the future, /// but not necessarily while <see cref="Write"/> is running. /// </remarks> public void Write(uint realm, Hash hash, int length, WithSpan.ReadWrite writer) { var addr = _wheel.ScheduleWrite(realm, hash, length, writer); _index.Add(realm, hash, addr); }