Ian, I think MS dug their own hole when they added the Seek() method to Stream that allows use of a signed offset and anything other than SeekOrigin.Current.
Adding a method to asynchronously write and seek in one function adds nothing to the interface that Stream currently provides. The fact that this fictional function may provide a concurrent-friendly (or "atomic") implementation is just that: an implementation detail. Conversely, the fact that calling Seek() then BeginWrite() is not concurrent-friendly is also an implementation detail; but implied in the point that Stream is documented as not thread-safe. I would argue with the original post that a new method is required to make Stream (or FileStream in that context) thread-safe. I think the current Stream.Seek() is a pollution of a stream abstraction that has been kludged with the likes of CanSeek(). On Sat, 17 Dec 2005 14:00:57 -0000, Ian Griffiths <[EMAIL PROTECTED]> wrote: >It seems to me that there's a clear difference between what Seek gives >you and what you're asking for. (Otherwise you wouldn't be asking for >it.) I admit that I chose poor terminology to describe the feature: I >should have said "concurrent random access" rather than just "random >access". But I stand by my assertion that it shouldn't be in Stream. > >The problem I have with adding this to the Stream abstraction is that >all of a sudden, *every* Stream has to support it. > >So you undoubtedly *could* have something not dissimilar to Stream that >support concurrent random access. But if you made this a feature of >Stream you just made it mandatory for every implementation of Stream to >support this. > >That makes it a non-starter for where we are today - this suggestion >will never fly because Stream shipped almost 4 years ago, and there's no >way to go and add this functionality retrospectively to all the Streams >everyone has implemented ever since. > >Given where we are, the only way to add this functionality would be to >introduce a new more specialized abstraction. (Either specifically to >files, or possibly as some new AsyncStream abstraction.) > >So this leaves this discussion as an academic question: should Stream >have had this functionality from the start? > >I think Stream's better without this, because this extra feature would >increase the cost of implementing Stream. And this is the tradeoff you >often get with abstractions: making them rich enough to be useful but >simple enough that anyone can be bothered to implement them. > >Mandating that all streams that support seeking also support concurrent >random access seems like a high tax to impose on implementing Stream. > >If everyone were clamouring for it, then it'd be reasonable. But >they're not. I've worked on a wide range of .NET projects in the years >since .NET shipped, and while I can appreciate in abstract that this >would be a nice feature, I've never run into a scenario where this would >have been useful in practice. (But I have run into scenarios where I >wanted to implement Stream. So this feature would have been a net loss >for me.) > >More tellingly, you rarely see anyone asking for it on public forums. As >far as I can tell from the various mailing lists, newsgroups, and forums >I've hung out on, my experience of never having come across a concrete >need for this is more common than your experience. > >Which suggests that Microsoft made the right decision: Stream would have >been the wrong place to put support for concurrent random access. > >So even if you were starting from a clean slate, and wanted v1.0 of the >framework to offer this functionality (and I agree it would be nice to >have, despite the fact that I've never actually needed it) I still think >it would be a mistake to put something this specialized into something >as generic as Stream. > >It's at best a poor match (and at worst, inappropriate) for most >implementations of Stream. For the implementations of Stream for which >it's appropriate, it's not useful most of the time. > >To steal a phrase that Ron Jeffries recently used on the TDD mailing >list: > > " it would be a bit like having a #10 Torx driver as part > of my hammer. Occasionally useful but also an odd addition." > > >Of course it's hugely frustrating if you happen to hit the case where >you want to do something that you know would be pretty easy for the >underlying implementation you happen to be using. But that doesn't >translate into an argument that Stream is designed wrong. > > >-- >Ian Griffiths >http://www.interact-sw.co.uk/iangblog/ > >> -----Original Message----- >> From: John Davis >> >> Yes, I disagree with your assertion stating the Stream interface is >the >> wrong abstraction for random access because it already contains a Seek >> method. The random access is already there. >> >> So, for streams where (CanSeek == true) there should also be a >> BeginRead/Write(file_offset, ...) which is threadsafe. >> >> Yes, you're right the docs say it isn't threadsafe. But for heaven >sake, >> surely we can add a class that is threadsafe for file io in the same >way >> the >> Win32 API provides. How are people supposed to write high throughput >> server >> software without a good async managed file io class? Apparently >everyone >> is >> still using C++. >> >> As you may have guessed, I've already hit this on my current project. >> Lots >> of locks to compensate for the above oversight. >> >> >From: Ian Griffiths <[EMAIL PROTECTED]> >> >Reply-To: "Discussion of advanced .NET topics." >> ><[email protected]> >> >To: [email protected] >> >Subject: Re: [ADVANCED-DOTNET] Possible defect in BeginWrite on >> FileStream >> >Date: Thu, 15 Dec 2005 22:42:36 -0000 >> > >> >John Davis wrote: >> > > Ian I'm afraid you're really mistaken on this one. >> > >> >Which bit do you disagree with? >> > >> >Do you disagree that Stream is the wrong abstraction for random >access? >> > >> >Do you disagree that if a class is documented as not safe for >> >multi-threaded access, then it doesn't really matter what new methods >> >you add, you still can't use it from multiple threads? >> > >> >Do you disagree with the claim that Stream offering stream-like >> >behaviour is a design limitation and not an actual defect? (The >absence >> >of a random access API might be regarded as a defect, but that >doesn't >> >mean there's necessarily a problem with the stream API - it does what >> >it's designed to do.) >> > >> >You've obviously thought long and hard about it - almost 7 months >> >according to the email timestamps. :) But I can't tell what I said >that >> >you think I'm "really mistaken on". >> > >> >I'm not disagreeing that random access (as opposed to stream-like >> >access) would be nice to have. (Although it's not something I happen >to >> >have run into a need for in any .NET projects yet.) I'm just saying >> >Stream's the wrong place for it. >> > >> >In particular, bear in mind that not all Streams are implemented on >top >> >of something that necessarily has intrinsic OVERLAPPED-like >> >functionality. (Not all streams are files or file-like handles.) >> > >> > >> > >> > > What I'm looking for is the equivalent of a ReadFile or WriteFile >> > > call here the offset is passed in the OVERLAPPED struct. >> > >> >I agree there are circumstances in which this would be handy. But I >> >stand by my assertions that Stream is the wrong abstraction for this. >> >Not every Stream is a file. Stream is not meant to be an abstract >> >representation of a file - it's more widely applicable than that. >(Hence >> >its narrower API - the more specific an API an abstraction has, the >less >> >broadly applicable it is.) >> > >> > >> > > The call can occur through overlapped IO, or an IO completion >> > > port. The call is also atomic. >> > >> >And it's not presenting a stream-like abstraction. I think that's at >> >the heart of the issue. >> > >> > >> > >> > > There needs to be a random access async read/write of some >> > > sort which uses the OVERLAPPED struct in the Win32 API in that >> > > it has a file offset field for the read or write. >> > >> >Sure, just probably not Stream. >> > >> >I guess if you changed nature of the Stream API by adding new >> >non-streamish functionality and also changed the classes so they >> >permitted concurrent access then that'd solve the problem. (Except >for >> >the fact that this might break a lot of existing streams because they >> >don't implement this feature right now...) >> > >> >But aren't you introducing a whole new abstraction? >> > >> > >> > > If one observes the current implementation in >> > > DotNetReflector, the OVERLAPPED struct is getting populated >> > > via the FileStream Seek call. The OVERLAPPED struct is then >> > > used on the next async write. Why implement it this way if >> > > FileStreams can only be used by a single thread? >> > >> >Err...isn't that a distinctly sequential approach to the problem? It >> >presumes that the call to Seek and the async write will follow on one >> >after the other without interference. >> > >> >You'd need a completely different implementation if you wanted to >> >support non-sequential use wouldn't you? >> > >> > >> >-- >> >Ian Griffiths >> > >> > >> >On Thu, 26 May 2005 23:22:45 +0100, Ian Griffiths >> ><[EMAIL PROTECTED]> >> >wrote: >> > >> > >Even if there were an atomic method how would that help? It >wouldn't >> > >eliminate the race condition, because it would still be illegal to >call >> > >the method simultaneously on multiple threads. (FileStream's >> > >documentation says it's illegal to do that.) So you'd still need >to >> >use >> > >locking or some other synchronization method to make sure your >threads >> > >take it in turns even if there were an atomic method. >> > > >> > >I agree it would be cleaner to have a random access method if >random >> > >access is what you're doing - a stream isn't really the right >> > >abstraction in that case. (Sadly it's the only one we're offered >> > >AFAIK.) >> > > >> > >But is this a defect? It's a bug to use a stream from multiple >threads >> > >without synchronization. It would still be a bug even with the >atomic >> > >method you propose. >> > > >> > > >> > >-- >> > >Ian Griffiths >> > >http://www.interact-sw.co.uk/iangblog/ >> > > >> > >> -----Original Message----- >> > >> From: John Davis >> > >> >> > >> >Are you saying that locking, Seeking, BeginWriteing, and >unlocking >> >is >> > >> not good enough? >> > >> >> > >> Well, I guess it is good enough, just doesn't seem very elegant. >> > >Would be >> > >> nice if there was an offset in there so one "atomic" call could >be >> > >made. >> > >> Same as with Win32 overlapped io. >> > >> >> > >> >Does the BeginWrite not in effect take a snapshot of >> > >> the seek location when the operation starts? >> > >> >> > >> I don't know for certain if it does, I certainly hope so. Time >to go >> > >> digging with reflector I guess. >> > >> >> > >> Ian Griffiths <[EMAIL PROTECTED]> wrote: >> > >> The documentation says that members of this class are not thread >> >safe, >> > >> so multiple threads shouldn't be using this at any given >instance. >> >You >> > >> need to do some kind of locking if you have multiple threads >using >> >it. >> > >> >> > >> Are you saying that locking, Seeking, BeginWriteing, and >unlocking is >> > >> not good enough? Does the BeginWrite not in effect take a >snapshot of >> > >> the seek location when the operation starts? >> > >> >=================================== >> >This list is hosted by DevelopMentor(r) http://www.develop.com >> > >> >View archives and manage your subscription(s) at >> http://discuss.develop.com >> >> =================================== >> This list is hosted by DevelopMentor(r) http://www.develop.com >> >> View archives and manage your subscription(s) at >> http://discuss.develop.com > >=================================== >This list is hosted by DevelopMentorĀ® http://www.develop.com > >View archives and manage your subscription(s) at http://discuss.develop.com =================================== This list is hosted by DevelopMentorĀ® http://www.develop.com View archives and manage your subscription(s) at http://discuss.develop.com
