private async Task <FileIOResult> ReadAsyncCore(byte[] buffer, int offset, int size, MessageLoop messageLoop = null) { var tcs = new TaskCompletionSource <FileIOResult>(); EventHandler <FileIOResult> handler = (s, e) => { tcs.TrySetResult(e); }; try { HandleReadData += handler; if (MessageLoop == null && messageLoop == null) { Read(buffer, offset, size); } else { Action <PPError> action = new Action <PPError>((e) => { var result = (PPError)PPBFileIO.Read(this, offset, buffer, size, new BlockUntilComplete() ); tcs.TrySetResult(new FileIOResult((int)result, (int)result == 0)); } ); InvokeHelper(action, messageLoop); } return(await tcs.Task); } catch (Exception exc) { Console.WriteLine(exc.Message); tcs.SetException(exc); return(new FileIOResult((int)PPError.Aborted, true)); } finally { HandleReadData -= handler; } }
/// <summary> /// Reads from an offset in the file. /// /// The size of the buffer must be large enough to hold the specified number /// of bytes to read. This function might perform a partial read, meaning /// that all the requested bytes might not be returned, even if the end of the /// file has not been reached. /// /// This function reads into a buffer that the caller supplies. This buffer /// must remain valid as long as the FileIO resource is alive. If you use /// a completion callback factory and it goes out of scope, it will not issue /// the callback on your class, BUT the callback factory can NOT cancel /// the request from the browser's perspective. This means that the browser /// will still try to write to your buffer even if the callback factory is /// destroyed! /// /// So you must ensure that your buffer outlives the FileIO resource. If you /// have one class and use the FileIO resource exclusively from that class /// and never make any copies, this will be fine: the resource will be /// destroyed when your class is. But keep in mind that copying a pp::FileIO /// object just creates a second reference to the original resource. For /// example, if you have a function like this: /// FileIO MyClass.GetFileIO(); /// where a copy of your FileIO resource could outlive your class, the /// callback will still be pending when your class goes out of scope, creating /// the possibility of writing into invalid memory. So it's recommended to /// keep your FileIO resource and any output buffers tightly controlled in /// the same scope. /// /// <strong>Caveat:</strong> This Read() is potentially unsafe if you're using /// an EventHandler to scope callbacks to the lifetime of your /// class. When your class goes out of scope, the native callback factory will not /// actually cancel the callback, but will rather just skip issuing the /// callback on your class. This means that if the FileIO object outlives /// your class (if you made a copy saved somewhere else, for example), then /// the browser will still try to write into your buffer when the /// asynchronous read completes, potentially causing a crash. /// /// See the other version of Read() which avoids this problem by writing into /// ArraySegment, where the output buffer is automatically managed by the native callback. /// /// </summary> /// <param name="buffer">The buffer to hold the specified number of bytes read.</param> /// <param name="offset">The offset into the file.</param> /// <param name="bytesToRead">The number of bytes to read from <code>offset</code>.</param> /// <returns>Error code. If the return value is 0, then end-of-file was /// reached.</returns> public PPError Read(byte[] buffer, int offset, int bytesToRead) => (PPError)PPBFileIO.Read(this, offset, buffer, bytesToRead, new CompletionCallback(OnReadData));