Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are Stream.ReadAsync and Stream.WriteAsync supposed to alter the cursor position synchronously before returning or after the operation completes?

I've been attempting to implement a Stream that supports ReadAsync and WriteAsync, and given the spareseness of the documentation, I'm struggling to understand how to do so properly. Specifically, with respect to the stream's cursor position. A similar question was asked here and here regarding the old BeginRead function. The documentation for that function seemed to indicate that BeginRead should not be called again until any pending asynchronous operations had completed.

Given that BeginRead is now deprecated no longer recommended for new development and Stream has likely been significantly altered to implement the new Async functions, things are once again unclear. (EDIT: Usually this kind of warning means that the new functions are implemented directly and the old functions call the new ones and are only still there for backwards compatibility, but that appears to not quite be the case here).

The ReadAsync and WriteAsync functions are defined such that they don't take the desired read/write stream position as their Win32 counterparts do (a very poor design choice in my opinion), but instead rely on the current position held by the stream implementation. That situation is fine if one of two conditions hold:

  1. ReadAsync and WriteAsync must grab the current cursor position for use by the operation and update it as if the operation completed (or not update it at all) before they return the Task, or
  2. No calls to ReadAsync or WriteAsync can be made until all previous async calls have been completed.

Outside of those two conditions, the caller can never be sure of the position the read or write will occur at because pending asynchronous operations could alter the position of the stream in between any Seek and call to ReadAsync or WriteAsync. Neither of these conditions is documented as a requirement, so I'm left to wonder how it's supposed to function.

My whitebox testing seems to indicate that at least for the FileStream version of Stream, the stream position updates asynchronously, which would seem to indicate that the second condition (only one pending operation allowed) is still the one that is required, but that seems like a serious limitation (it certainly precludes any kind of internal scatter-gather implementation).

Can anyone provide any kind of authoritative information as to whether the old BeginRead limitation still applies to ReadAsync or not?

like image 683
James Avatar asked May 25 '15 18:05

James


1 Answers

Can anyone provide any kind of authoritative information as to whether the old BeginRead limitation still applies to ReadAsync or not?

The same limitations apply for BeginRead and ReadAsync.

The old APM methods haven't been deprecated. They are still fully supported and there's nothing wrong with using them. However, the async methods are considerably easier to use so the documentation suggests using them instead.

All these async "overloads" on these old classes usually do still consist of calling BeginXXX and EndXXX or at most both options call a shared method (e.g. FileStream.BeginReadAsync). I have never seen any code (in the framework or otherwise) that has APM wrapper methods over the async one.

Therefore, calling ReadAsync will result in calling BeginRead so any limitation applies to both. Furthermore, since Stream isn't thread-safe and doesn't advertise as being concurrent-safe (which is slightly different) it's safe to assume you can't flood it with async requests concurrently.

like image 190
i3arnon Avatar answered Sep 24 '22 10:09

i3arnon