Пример #1
0
            public void OnCompletion(FileAsyncIOResult asyncIOResult)
            {
                m_result = asyncIOResult;

                Contract.Assume(m_pinningHandle.IsAllocated);
                m_pinningHandle.Free();

                m_event.Set();
            }
Пример #2
0
        /// <inheritdoc />
        public unsafe Overlapped *ReadFileOverlapped(
            IIOCompletionTarget target,
            SafeFileHandle handle,
            byte *pinnedBuffer,
            int bytesToRead,
            long fileOffset)
        {
            Contract.Requires(target != null);
            Contract.Requires(handle != null && !handle.IsInvalid);
            Contract.Requires(pinnedBuffer != null);

            TaggedOverlapped *overlapped = m_overlappedPool.ReserveOverlappedWithTarget(target);

            TraceStartIfEnabled(overlapped, target);

            bool needOverlappedRelease     = true;
            bool overlappedHasBeenReleased = false;

            try
            {
                FileAsyncIOResult result = FileSystemWin.ReadFileOverlapped(
                    handle,
                    pinnedBuffer,
                    bytesToRead,
                    fileOffset,
                    (Overlapped *)overlapped);

                if (result.Status != FileAsyncIOStatus.Pending)
                {
                    Contract.Assert(
                        result.Status == FileAsyncIOStatus.Succeeded ||
                        result.Status == FileAsyncIOStatus.Failed);

                    // We could call the target directly.
                    // However, since the target may itself issue more I/O, we need to prevent unbounded stack growth.
                    // TODO: We could set a recursion-limit to allow some fraction of repeated IOs to complete synchronously, without
                    //       queueing to the threadpool. Sync completions are the common case for files cached in memory.
                    ReleaseOvelappedAndQueueCompletionNotification(overlapped, result);
                    overlappedHasBeenReleased = true;
                }

                // At this point overlapped is either needed (pending status)
                // or already released by ReleaseOvelappedAndQueueCompletionNotification
                needOverlappedRelease = false;

                return(!overlappedHasBeenReleased ? (Overlapped *)overlapped : null);
            }
            finally
            {
                if (needOverlappedRelease)
                {
                    IIOCompletionTarget releasedTarget = ReleaseOverlappedAndGetTarget(overlapped);
                    Contract.Assume(releasedTarget == target);
                }
            }
        }
Пример #3
0
        private unsafe void QueueCompletionNotification(IIOCompletionTarget target, FileAsyncIOResult result)
        {
            var notificationArgs = new IOCompletionNotificationArgs
            {
                Result = result,
                Target = target,
            };

            ThreadPool.QueueUserWorkItem(
                state =>
            {
                var stateArgs = (IOCompletionNotificationArgs)state;
                stateArgs.Target.OnCompletion(stateArgs.Result);
            },
                notificationArgs);
        }
Пример #4
0
        /// <inheritdoc />
        public unsafe Overlapped *ReadOverlapped(
            IIOCompletionTarget target,
            byte *pinnedBuffer,
            int bytesToRead,
            long fileOffset)
        {
            FileAsyncIOResult result;

            if (GetCurrentLength() == 0 || fileOffset >= GetCurrentLength())
            {
                result = new FileAsyncIOResult(FileAsyncIOStatus.Failed, bytesTransferred: 0, error: NativeIOConstants.ErrorHandleEof);
            }
            else
            {
                lock (m_fileStream)
                {
                    try
                    {
                        byte[] buffer = new byte[bytesToRead];
                        m_fileStream.Seek(fileOffset, SeekOrigin.Begin);
                        int bytesRead = m_fileStream.Read(buffer, 0, bytesToRead);

                        for (int i = 0; i < bytesRead; i++)
                        {
                            pinnedBuffer[i] = buffer[i];
                        }

                        result = new FileAsyncIOResult(FileAsyncIOStatus.Succeeded, bytesTransferred: bytesRead, error: NativeIOConstants.ErrorSuccess);
                    }
                    catch (Exception ex)
                    {
                        int errorCode = (int)NativeErrorCodeForException(ex);
                        result = new FileAsyncIOResult(FileAsyncIOStatus.Failed, bytesTransferred: 0, error: errorCode);
                    }
                }
            }

            QueueCompletionNotification(target, result);
            return(null);
        }
Пример #5
0
        public async Task ReadEmptyFile()
        {
            string path = GetFullPath("file");

            using (File.Create(path))
            {
            }

#if NET_FRAMEWORK
            using (var io = new IOCompletionManager())
#else
            IIOCompletionManager io = null;
#endif
            {
                using (
                    IAsyncFile file = AsyncFileFactory.CreateOrOpen(
                        path,
                        FileDesiredAccess.GenericRead,
                        FileShare.Read | FileShare.Delete,
                        FileMode.Open,
                        FileFlagsAndAttributes.None,
                        io))
                {
                    XAssert.IsTrue(file.CanRead);
                    XAssert.IsFalse(file.CanWrite);

                    var buffer = new byte[10];
                    FileAsyncIOResult result = await file.ReadAsync(buffer, buffer.Length, 0);

                    XAssert.AreEqual(FileAsyncIOStatus.Failed, result.Status);
                    XAssert.IsTrue(result.ErrorIndicatesEndOfFile);

                    result = await file.ReadAsync(buffer, buffer.Length, 16);

                    XAssert.AreEqual(FileAsyncIOStatus.Failed, result.Status);
                    XAssert.IsTrue(result.ErrorIndicatesEndOfFile);
                }
            }
        }
Пример #6
0
        public void ReadFileToEnd()
        {
            const int NumberOfWords = 1024 * 1024;
            const int TotalSize     = NumberOfWords * 4;

            string path = GetFullPath("file");

            using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Delete))
                using (var writer = new BinaryWriter(fs, Encoding.UTF8, leaveOpen: true))
                {
                    for (int i = 0; i < NumberOfWords; i++)
                    {
                        writer.Write((int)i);
                    }
                }

            using (var memoryStream = new MemoryStream(capacity: NumberOfWords * 4))
            {
                using (var io = new IOCompletionManager())
                    using (var target = new BlockingCompletionTarget(bufferSize: 64 * 1024))
                        using (
                            SafeFileHandle handle = CreateOrOpenFile(
                                path,
                                FileDesiredAccess.GenericRead,
                                FileShare.Read | FileShare.Delete,
                                FileMode.Open))
                        {
                            io.BindFileHandle(handle);

                            while (true)
                            {
                                unsafe
                                {
                                    io.ReadFileOverlapped(target, handle, target.PinBuffer(), target.Buffer.Length, fileOffset: memoryStream.Length);
                                }

                                FileAsyncIOResult result = target.Wait();

                                if (result.Status == FileAsyncIOStatus.Failed)
                                {
                                    XAssert.IsTrue(result.ErrorIndicatesEndOfFile, "Unexpected non-EOF read failure.");
                                    break;
                                }
                                else
                                {
                                    XAssert.AreEqual(FileAsyncIOStatus.Succeeded, result.Status);
                                }

                                XAssert.AreNotEqual(0, result.BytesTransferred);
                                XAssert.IsTrue(
                                    memoryStream.Length + result.BytesTransferred <= TotalSize,
                                    "Too many bytes read; Read {0} so far and just got another {1} bytes",
                                    memoryStream.Length,
                                    result.BytesTransferred);
                                memoryStream.Write(target.Buffer, 0, result.BytesTransferred);
                            }
                        }

                memoryStream.Position = 0;

                using (var reader = new BinaryReader(memoryStream, Encoding.UTF8, leaveOpen: true))
                {
                    for (int i = 0; i < NumberOfWords; i++)
                    {
                        XAssert.AreEqual(i, reader.ReadInt32(), "File contents read incorrectly; offset {0}", i * 4);
                    }
                }
            }
        }
Пример #7
0
        public void ConcurrentReadsSingleBatch()
        {
            const int NumberOfConcurrentReads = 128; // This should be larger than the batch size at which we allocate OVERLAPPED structures.

            WithPipePair(
                (server, client) =>
            {
                using (var io = new IOCompletionManager())
                {
                    io.BindFileHandle(client);

                    var targets       = new BlockingCompletionTarget[NumberOfConcurrentReads];
                    var completions   = new bool[NumberOfConcurrentReads];
                    var bufferToWrite = new byte[NumberOfConcurrentReads * 4];

                    using (var writeBufferStream = new MemoryStream(bufferToWrite, writable: true))
                    {
                        using (var writer = new BinaryWriter(writeBufferStream))
                        {
                            for (int i = 0; i < NumberOfConcurrentReads; i++)
                            {
                                writer.Write((int)i);
                            }
                        }
                    }

                    try
                    {
                        for (int i = 0; i < targets.Length; i++)
                        {
                            targets[i] = new BlockingCompletionTarget(bufferSize: 8);
                            unsafe
                            {
                                // Note that non-seekable streams mandate an offset of 0 for all requests.
                                // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684342%28v=vs.85%29.aspx
                                io.ReadFileOverlapped(targets[i], client, targets[i].PinBuffer(), 4, fileOffset: 0);
                            }
                        }

                        server.Write(bufferToWrite, 0, bufferToWrite.Length);
                        server.Flush();

                        for (int i = 0; i < targets.Length; i++)
                        {
                            FileAsyncIOResult result = targets[i].Wait();
                            XAssert.AreEqual(FileAsyncIOStatus.Succeeded, result.Status);
                            XAssert.AreEqual(result.BytesTransferred, 4);
                            int completedValue = BitConverter.ToInt32(targets[i].Buffer, 0);

                            XAssert.IsTrue(completedValue >= 0 && completedValue < completions.Length);
                            XAssert.IsFalse(completions[completedValue], "Value {0} completed multiple times", completedValue);
                            completions[completedValue] = true;
                        }
                    }
                    finally
                    {
                        foreach (BlockingCompletionTarget target in targets)
                        {
                            if (target != null)
                            {
                                target.Dispose();
                            }
                        }
                    }
                }
            });
        }
Пример #8
0
        /// <summary>
        /// This is the async callback function. Only one thread could/should call this.
        /// </summary>
        void IIOCompletionTarget.OnCompletion(FileAsyncIOResult asyncIOResult)
        {
            Contract.Assume(asyncIOResult.Status != FileAsyncIOStatus.Pending);

            bool cancel = false;

            lock (m_lock)
            {
                switch (m_state)
                {
                case State.Initialized:
                    Contract.Assume(false, "Read completion can only happen once in the Reading state");
                    throw new InvalidOperationException("Unreachable");

                case State.Reading:
                    break;

                case State.Stopping:
                    // If Stopping, we had to defer reaching Stopped until an outstanding read completed - which is now.
                    // We throw away remaining data in the pipe (call WaitForEofAsync if all data is needed before dispose).
                    // Any current waiters for WaitForEofAsync will also resume (though possibly with an exception, due to this pre-EOF cancelation).
                    OnStopped();

                    // Signaling cancelation needs to happen outside of the lock (otherwise continuations on the completion source
                    // may run synchronously, and have the lock by accident).
                    cancel = true;
                    break;

                case State.Stopped:
                    Contract.Assume(false, "The Stopped state should not be reached while a read is outstanding");
                    throw new InvalidOperationException("Unreachable");

                default:
                    throw Contract.AssertFailure("Unhandled state");
                }
            }

            if (cancel)
            {
                SignalCompletion(reachedEof: false);
                return;
            }

            int byteLen;

            if (asyncIOResult.Status == FileAsyncIOStatus.Failed)
            {
                // Treat failures as EOF.
                // TODO: This is a bad thing to do, but is what the original AsyncStreamReader was doing.
                byteLen = 0;
            }
            else
            {
                Contract.Assume(!asyncIOResult.ErrorIndicatesEndOfFile, "End-of-file appears as a failure status");
                byteLen = asyncIOResult.BytesTransferred;
                Contract.Assume(byteLen > 0);
            }

            if (byteLen == 0)
            {
                // We're at EOF, we won't call this function again from here on.
                lock (m_lock)
                {
                    if (StringBuilderInstace.Length != 0)
                    {
                        m_messageQueue.Enqueue(StringBuilderInstace.ToString());
                        StringBuilderInstace.Length = 0;
                    }

                    m_messageQueue.Enqueue(null);
                }

                try
                {
                    // UserCallback could throw, but we should still signal EOF
                    try
                    {
                        FlushMessageQueue();
                    }
#pragma warning disable ERP022 // Unobserved exception in generic exception handler
                    catch
                    {
                        // We are in an invalid state (maybe the process got killed or crashed).
                        // As the comment above says the could throw.
                        // Make sure we are catching the exception without crashing.
                    }
#pragma warning restore ERP022 // Unobserved exception in generic exception handler
                }
                finally
                {
                    lock (m_lock)
                    {
                        // No more reads or callbacks will occur, so enter the Stopped state and clean up.
                        OnStopped();
                    }

                    m_messageQueue = null;
                    SignalCompletion(reachedEof: true);
                }
            }
            else
            {
                int charLen = m_decoder.GetChars(ByteBuffer, 0, byteLen, CharBuffer, 0);
                GetLinesFromCharBuffers(charLen);

                // File offset is ignored since we're reading a pipe.
                m_file.ReadOverlapped(this, m_byteBufferPtr, m_byteBufferSize, fileOffset: 0);
            }
        }
Пример #9
0
        public async Task ReadFileRandomAccess()
        {
            const int NumberOfReads        = 16;
            const int NumberOfWordsPerRead = 64 * 1024;
            const int NumberOfBytesPerRead = NumberOfWordsPerRead * 4;
            const int NumberOfWords        = NumberOfWordsPerRead * NumberOfReads;
            const int TotalSize            = NumberOfWords * 4;

            string path = GetFullPath("file");

            using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Delete))
            {
                using (var writer = new BinaryWriter(fs, Encoding.UTF8, leaveOpen: true))
                {
                    for (int i = 0; i < NumberOfWords; i++)
                    {
                        writer.Write((int)i);
                    }
                }
            }

#if NET_FRAMEWORK
            using (var io = new IOCompletionManager())
#else
            IIOCompletionManager io = null;
#endif
            {
                using (
                    IAsyncFile file = AsyncFileFactory.CreateOrOpen(
                        path,
                        FileDesiredAccess.GenericRead,
                        FileShare.Read | FileShare.Delete,
                        FileMode.Open,
                        FileFlagsAndAttributes.None,
                        io))
                {
                    XAssert.IsTrue(file.CanRead);
                    XAssert.IsFalse(file.CanWrite);

                    var readBuffer = new byte[TotalSize];
                    var readTasks  = new Task[NumberOfReads];
                    for (int i = 0; i < readTasks.Length; i++)
                    {
                        int offset = NumberOfBytesPerRead * i;
                        readTasks[i] = Task.Run(
                            async() =>
                        {
                            byte[] localBuffer = new byte[NumberOfBytesPerRead];
                            int readSoFar      = 0;
                            while (readSoFar < NumberOfBytesPerRead)
                            {
                                FileAsyncIOResult result =
                                    await file.ReadAsync(localBuffer, bytesToRead: NumberOfBytesPerRead - readSoFar, fileOffset: offset + readSoFar);
                                XAssert.AreEqual(FileAsyncIOStatus.Succeeded, result.Status);
                                XAssert.IsTrue(result.BytesTransferred > 0);
                                XAssert.IsTrue(readSoFar + result.BytesTransferred <= NumberOfBytesPerRead);

                                Buffer.BlockCopy(localBuffer, 0, readBuffer, offset + readSoFar, result.BytesTransferred);
                                readSoFar += result.BytesTransferred;
                            }

                            Contract.Assert(readSoFar == NumberOfBytesPerRead);
                        });
                    }

                    for (int i = 0; i < readTasks.Length; i++)
                    {
                        await readTasks[i];
                    }

                    using (var reader = new BinaryReader(new MemoryStream(readBuffer, writable: false), Encoding.UTF8, leaveOpen: false))
                    {
                        for (int i = 0; i < NumberOfWords; i++)
                        {
                            XAssert.AreEqual(i, reader.ReadInt32());
                        }
                    }
                }
            }
        }
Пример #10
0
        private unsafe void ReleaseOvelappedAndQueueCompletionNotification(TaggedOverlapped *overlapped, FileAsyncIOResult result)
        {
            // We can now find the external-code callback that needs notification for this request.
            // We can't allow it to block this thread, which is dedicated to servicing the completion port.
            // Were it to block, we could starve I/O completion; blocking on other I/O completions could be a deadlock.
            // So, we guarantee that IIOCompletionTargets run on thread-pool threads - they suffer no special restrictions.
            IIOCompletionTarget target = ReleaseOverlappedAndGetTarget(overlapped);

            var notificationArgs = new IOCompletionNotificationArgs
            {
                Result = result,
                Target = target,
            };

            ThreadPool.QueueUserWorkItem(
                state =>
            {
                var stateArgs = (IOCompletionNotificationArgs)state;
                stateArgs.Target.OnCompletion(stateArgs.Result);
            },
                notificationArgs);
        }