Exemplo n.º 1
0
        /// <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];
Exemplo n.º 2
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");
        }
Exemplo n.º 3
0
        /// <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);
        }