Does that mean that we can do away with synchronized read and write operations (ignore),
Other operations, in particular those that take an explicit position, may proceed concurrently; whether they in fact do so is dependent upon the underlying implementation and is therefore unspecified.
and worry about only the concurrency on the server.
As my assignment states that "I can assume that only one program is accessing the database at any time". So the client checks the record lock in memory for update and delete operations, obtains it and does a transaction with the database. Hence no need to synchronize the read/write operations.
"I'm not back." - Bill Harding, Twister
Originally posted by Jim Yingst:
Greetings, everyone. Once more into the Philippe is correct to be wary of FileChannel's thread safety, IMO - the guarantees it offers are not absolute.
"I'm not back." - Bill Harding, Twister
You may assume that at any moment, at most one program is accessing the database file; therefore your locking system only needs to be concerned with multiple concurrent clients of your server.
"I'm not back." - Bill Harding, Twister
Incidentally, since I used read(ByteBuffer) above - I also believe that FileChannel offers no guarantee that the ByteByffer will be filled, even if the file has more bytes.
Originally posted by Jim Yingst:
Howdy, Max.
Actually, you can trust the guarantees, but you have to be careful about reading too much into them.
I agree with this. When I said "the guarantees it offers are not absolute" I meant that the guarantees do not promise everything we might think or hope that they promise, and we must be careful with them. I do believe that the guarantees are obeyed (probably, excepting some possible bugs which will be fixed soon if not already).
For example, a write is atomic, if you don't move the position explicitly, and allow the FileChannel to write to it's next natural position. Jim, this is all on pages 284-285 of my book, so you should know this
I read this, but don't believe it offers the level of security you think it does. We need to provide random access to any record in the file, not just the next record. So either we have to use read/write methods that take an explicit position, or we set the position and then use a read/write that implicitly uses that position. You seem to advocate the latter approach. And it's true that the atomicity of the read/write is guarantted by the API. But what is not guaranteed is that there will be no interruption btween setting the position and performing the read write. That is, if we do
channel.position(100);
channel.read(buffer1);
while another thread does
channel.position(200);
channel.read(buffer2);
we may end up with something like
channel.position(100);
channel.position(200);
channel.read(buffer2);
channel.read(buffer1);
Here buffer2 gets what buffer1 was expecting, and buffer1 gets whatever is after the record at 200. This is no good; we'd need explicit synchronization to prevent interruption between the position() and read() methods.
Alternately, we can use the read/write methods that take an explicit position:
channel.read(buffer1, 100);
channel.read(buffer2, 200);
This works great as far as the reads are concerned. According to the API these methods may even process concurrently, but they're still guaranteed to each read from the appropriate place.
However, what if we have two thread doing:
channel.read(buffer1, 100);
channel.write(buffer2, 100);
According to the API these may also proceed concurrently. That's a potential problem. The read() will read from the correct position, but what it's reading may change underneath it. You may get a read that starts out with data from before the update, and ends with data from after the update. Unless, again, you protect your methods with synchronization.
I do believe that this type of dirty read is pretty unlikely, especially considering the record legnth is just 183 in my assignment. The system will probably finish each read/write atomically. And it's quite possible that on some particular implementations of FileChannel, this atomicity will always occur. However, it's not guaranteed by the FileChannel spec. So I advocate explicit synchronization if you want to ensure that dirty reads do not occur.
Incidentally, since I used read(ByteBuffer) above - I also believe that FileChannel offers no guarantee that the ByteByffer will be filled, even if the file has more bytes.
Originally posted by Bob Reeves:
Hi Jim:
In regard to your statement:
I also had this concern. FileChannel's read returns the number of bytes "actually" read. Unfortunately, this depends on what one's definition of actually, actually is. I noticed none of SUN's examples for FileChannle's read method enclose the call in a while loop, so I didn't use a while loop either. But, I'm not sure if looking at the implementation code is justified if the method contract doesn't guarantee the behavior.
I think SUN needs to put a Contract section in their Javadoc that explicitly states what behavior is guaranteed by a method. Fat chance?
Tx
"I'm not back." - Bill Harding, Twister
However, what if we have two thread doing:
channel.read(buffer1, 100);
channel.write(buffer2, 100);
According to the API these may also proceed concurrently. That's a potential problem. The read() will read from the correct position, but what it's reading may change underneath it. You may get a read that starts out with data from before the update, and ends with data from after the update. Unless, again, you protect your methods with synchronization.
I think there's a misunderstanding here. Reading changes the position of the of the FileChannel: thus, it's covered under the part of the documentation that says
Only one operation that involves the channel's position or can change its file's size may be in progress at any given time; attempts to initiate a second such operation while the first is still in progress will block until the first operation completes. Other operations, in particular those that take an explicit position, may proceed concurrently; whether they in fact do so is dependent upon the underlying implementation and is therefore unspecified.
Thus, the situation, as described, cannot occur.
public abstract int write(ByteBuffer src,
long position)
throws IOException
Writes a sequence of bytes to this channel from the given buffer, starting at the given file position.
This method works in the same manner as the write(ByteBuffer) method, except that bytes are written starting at the given file position rather than at the channel's current position.
This method does not modify this channel's position
Actually, it is in the documentation.
mean?Only one operation that involves the channel's position or can change its file's size may be in progress at any given time; attempts to initiate a second such operation while the first is still in progress will block until the first operation completes. Other operations, in particular those that take an explicit position, may proceed concurrently; whether they in fact do so is dependent upon the underlying implementation and is therefore unspecified.
means that you can use a FileChannel from multiple threads without worrying about disturbing its internal state unexpectedly. It doesn't not guarantee any resource that the FileChannel opeerates on.File channels are safe for use by multiple concurrent threads
Originally posted by Bob Reeves:
Hi Max:
This responds to your comment:
Respectfully, I disagree. My concern is that the method just might not return all the bytes requested in one read, whether another thread is involved or not. I've seen this behavior on Linux (in a single thread application) where a getBytes didn't return all the bytes known to be in the receive buffer. However, if I looped on a test byte count condition, then I obtained all the bytes.
So, I have this worry that if SUN says something like a method returns the number of bytes "actually" read, maybe the programmer should watch out.
Tx
Originally posted by Jim Yingst:
Hi, Max!
That's true for three out of four read methods recently surveyed. But you'll note that the code sample I provided (which Bob was responding to) uses
pos += channel.read(bytes, pos);
which does not change the FileChannel's position.
So, why not just use the implicit-position methods? Because they always rely on the assumption that the "current position" is the correct one, and that it hasn't just been reset by some other thread immediately beforehand. If multiple threads have access to a given FileChannel then the only way I see to prevent them from possibly interrupting each other like this is by using synchronization.
And if you're going to sync critical operations anyway, then it really doesn't matter much if the operation was atomic or not, does it? So really, I'm at a loss as to why the FileChannel API was written they way it is; the guarantees it does offer are insufficient to be really useful, IMO. I'm perfectly happy doing synchronization myself - I just wish the spec had been clearer
Originally posted by Vlad Rabkin:
Hi Jim,
Hi Max,
Jim said:
So, I beleive Jim is right. The methods (write/read) having position in argument DO NOT CHANGE position in the FileChannel, so the CAN proceed concurrently!
Vlad
"I'm not back." - Bill Harding, Twister
The point here is that there is only one way to get unstable data, and that is to use the FileChannel.read(byte[],position) method in an environment that allows multiple threads to access the same FileChannel. As I recall, you're not doing this, correct?
Originally posted by Philippe Maquet:
Hi Max,
Does it mean that if I have only one thread which performs all writes (they should be blocking, right ?) and multiple threads which use the FileChannel.read(byte[],position), I am OK ?
Thanks,
Phil.
Originally posted by Jim Yingst:
[QB]Hello again, Max.
You'll notice that FileChannel.write(byte[],position) can change file size.
Good catch - I had overlooked that. So OK, among read/write methods only read(ByteBuffer, long) seems to be eligible to proceed concurrently. Which is still enough to create problems multiple threads access a FileChannel using that method, if some sort of write() is being performed on the section of the file being read. If the write is changing XXXXXXXXXX to OOOOOOOOOO, the read could theoretically see something like XXXOOOOOOO. It's fairly unlikely, and maybe impossible on many/all platform for all I know. But it's exceedingly annoying to me that the spec leaves this hole in its guarantees.
Reads, all of them, block too. However, the read(byte[],position) method only blocks for other reads.
Eh? Where did that come from? I don't see why read(ByteBuffer, long) (assuming that's what you meant) would need to block for anything. Could be missing something again...
The point here is that there is only one way to get unstable data, and that is to use the FileChannel.read(byte[],position) method in an environment that allows multiple threads to access the same FileChannel.
Well, there are other problems if you use a shared FileChannel using implicit-position methods, because there can be interruptions between setting the position and reading or writing. I gave an example of this earlier in this thread. This won't lead to reading XXXOOOOOOO when XXXXXXXXXX or OOOOOOOOOO is expected - but it might lead to reading YYYYYYYYYY (a different record entirely) instead.
Unless, of course, we use synchronization. Which is actually pretty simple. Or, unless you use separate FileChannels as you prefer.
As I recall, you're not doing this [sharing FileChannels], correct?
Who - Vlad? Dunno, I've lost track. But there are a number of different people in this thread, and I know that some (like myself) aren't currently doing the just-in-time thing. The FileChannel question was first raised in this thread by S Bala, who seemed to be talking about multiple threads accessing a FileChannel, as are a number of the other people in this thread. If you want to talk about how FileChannel can be used safely in the contect of just-in-time FileChannel creation, that's fine - but I don't think this context has been clearly established in this thread for a lot of these comments. Many of the people reading this thread will not assume just-in-time creation unless it's explicitly stated.
For those people, I say don't think of FileChannel's operations as "atomic" because while many are, some are not, and there are just enough holes in the system to screw you up if you're not careful.
In contrast, syncronization is a simple and easy option, IMO:
Note that I'm still putting those reads and writes in loops. That's because of the other problem with FileChannel, mentioned in several other threads above. Even if reads / writes are atomic, there's no actual guarantee (that I can find) that a read or write will actually use all the bytes we expect.
That is, if there's still space in a buffer, and the file still has unread bytes, a read() may nonetheless return prematurely, without filling the buffer. At least, the API seems to imply this, and offers no guarantee otherwise.
Now, I haven't been able to actually observe this problem in testing. Maybe it never actually comes up. I know it comes up with FileInputStreams fairly often, so I tend to assume it's possible here unless guaranteed otherwise. And I would argue that if partial reads/writes are possible, then they actually destroy the safety of just-in-time FileChannels. It doesn't matter if each read() or write() is atomic, if it's possible for the method to complete without delivering all the intended bytes. You can put the method in a loop as I do above - but without synchronization, there's no way to prevent interruption between loop iterations.
And even with synchronization, if you're using just-in-time FileChannels then each FileChannel is a separate instance. You'd need to sync on some shared shared monitor. A static variable or some such.
In contrast, if you have one FileChannel shared by many threads, you can simply use synchronization as I do above to guarantee that each read or write will be uninterrupted. Synchronization is not the complex beast often it's made out to be if you just use it. It's true that in some situations synchronization can be a performance bottleneck. But as has been frequently observed here, performance need not be a significant concern for this assignment.
I acknowledge that most of my concerns here are very unlikely to manifest as observable problems in this assignment. And if someone wants to bypass synchronization because they consider the risks to be acceptably low for their purposes, that's fine, as an informed decision.
But if people believe that there are actual guarantees that this problems cannot occur, that's what I disagree with.
Originally posted by Bob Reeves:
Hi All:
Not another post on the FileChannel! Sorry, but I want to add one detail.
Vlad's comment about whether writing three bytes to the start of a long file might not cause a block seems reasonable. Not to soapbox, but perhaps it's not good to microread text that's not professionally written. The javadoc for FileChannel says something like any operation that can change the file length will block. It doesn't say "any operation that changes the file" will block. Maybe this is the emphasis the author intended. The purpose might be to increase the performance of write operations by permitting concurrency.
Tx
Originally posted by Bob Reeves:
Hi All:
Not another post on the FileChannel! Sorry, but I want to add one detail.
Vlad's comment about whether writing three bytes to the start of a long file might not cause a block seems reasonable. Not to soapbox, but perhaps it's not good to microread text that's not professionally written. The javadoc for FileChannel says something like any operation that can change the file length will block. It doesn't say "any operation that changes the file" will block. Maybe this is the emphasis the author intended. The purpose might be to increase the performance of write operations by permitting concurrency.
Tx
Originally posted by Vlad Rabkin:
Hi Max,
Hi Jim,
[Max] You'll notice that FileChannel.write(byte[],position) can change file size.
Well, I thought about that 100 times, but what does it exactly mean???
Let's say write with FileChannel.write(bytebuffer, position), where bytebuffer has only 3 bytes on the beginning of the file (So, I am sure that this operation will not change the size of the file). Theoretically each write can change the size of file, but in practically this one NOT. So, is atomicy guaranteed or not???
Vlad
But, the problem with Vlad point is that the compiler would have to examine your code, and know that you're only doing 3 bytes. Since it can't reasonably be expected to do this, then it has adhere to the specification. That doesn't mean that all methods always do so, but it's important to be clear that the method can't pick and choose when to adhere to spec and when not to.
Originally posted by Bob Reeves:
Hi Max:
This responds to your comment:
The motivation for by post was a decompile of FileChannelImpl, which is the type of the obect returned from RandomAccessFile's getChannel.
The decomile of FileChannelImpl shows no embedded JVM Monitor enter/JVM Monitor exit calls in any of the write methods. But all the position methods have them. Now FileChannelImpl calls IOUtil and FileDispatcher. Again neither of these contain Monitor calls in their write methods.
So, I think you can see why I conclude that the block on write must be in the native code. I'd also opinion that native code protects itself on block (ie. so that its file on disk information remains valid), not the user. Thus my conclusion.
Notice there is no customization of the compiled code for short buffers. Of course, the compiler couldn't do that! I think the native code looks at the buffer length, and branches according to file length. (Actually, the only time it must block is if the truncate method is called after the write. I'm guessing truncate isn't called frequently, so it might make sense to optimize this way.)
Tx
"I'm not back." - Bill Harding, Twister
"I'm not back." - Bill Harding, Twister
[Max]: Reads, all of them, block too. However, the read(byte[],position) method only blocks for other reads.
[Jim]: Eh? Where did that come from? I don't see why read(ByteBuffer, long) (assuming that's what you meant) would need to block for anything. Could be missing something again...
[Max]: if you follow the documention into it's obscure depths, you'll end up ]http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/ReadableByteChannel.html
Ah yes, the last paragraph of read()'s description. I had read that before and forgot about it. Thanks. You're right, reads are mutually atomic.
[Jim]: But it's exceedingly annoying to me that the spec leaves this hole in its guarantees.
[Max]: It depends on how you look at it. It could be a hole, or it could be the one way in which you can get right-now-dammit checking of the state of the file. There are times when that's exactly what you want. I think the wise folks @ Sun wanted to give you a way of checking under the hood @ your own peril, if you wanted to. Think of it this way: wouldn't you be ticked off if there was no way to do this at all?
That's true, there may be times when this sort of approach is useful. But if that was their intent here, I think they did a shoddy job of documenting it.
It seems that if Sun really wanted to make a method that just does a read ASAP, they did a poor job of it.
As such, I'm not really inclined to assume that what they said == what they meant. If it's possible that the implementation was shoddy, it's also possible the documentation was shoddy; perhaps the implementation is really perfect and they just did a poor job of explaining it. Maybe FileChannels really are thread-safe, but I'm having a hard time seeing that from the API they've provided.
[Max]: 7/8 of the methods are safe. The last is at your own risk.
Maybe. And, if we're trying to write code to access a FileChannel from multiple threads, then 6/8 of those methods are pretty much useless anyway.
(Unless you synchronize in which case you provide your own "thread safety".) Because all the implicit-position methods suffer from the fact that they have no control over whether another thread may interrupt in between setting the position() and doing the read() or write(). As shown in my second post above. (Not counting the intrusion into S Bala's post.)
I suppose that there might be some ways to use the implicit-position methods in a multi-thread context without synchronization, if we never use position() at all, and we have multiple threads doing reads on "whatever is at the current position" and we don't care which thread reads what. There are some applications I can imagine where this might be useful. So OK, replace "pretty much useless" above with "useless for random access to a file".
If you want to do a random-access read on a file without requiring synchronization, then read(ByteBuffer, long) is the one method that you want to be atomic. The other three read methods are no good anyway.
So it's particularly annoying that this is the one method that has a non-atomic exception, if there's also a write(ByteBuffer, long) being executed somewhere. Hence my frustration with the FileChannel API.
[Jim]: In contrast, syncronization is a simple and easy option, IMO:
[Max]: It's simple and easy, but it adds the spectre of nested locks. If you're safe threading advocate, as I am, you tremble at the mere thought of nested locks .
Not really. I make the synchronized blocks nice and small, and they don't call any code that could acquire another lock.
[Jim]: Even if reads / writes are atomic, there's no actual guarantee (that I can find) that a read or write will actually use all the bytes we expect.
That is, if there's still space in a buffer, and the file still has unread bytes, a read() may nonetheless return prematurely, without filling the buffer. At least, the API seems to imply this, and offers no guarantee otherwise.
[Max]: This is an incorrect statement, as shown http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/ReadableByteChannel.html.
OK, the write method does have an explicit guarantee; the read does not.
Taking the latter part first: I agree that it's possible read() isn't really susceptible to this problem at all. I haven't been able to demonstrate it in various tests I've run. But (a) I'm only testing on Windows 2000 Pro and XP Home at the moment, and (b) testing aside, the spec is still too vague.
As for "only one of the read methods is susceptible" - I disagree. Which one are you referring to? All four ultimately seem to inherit the same behavior specified by ReadableByteChannel's read(ByteBuffer), which does not guarantee how many bytes will be read. We can se what such a guarantee would look like by looking at the write(ByteBuffer) method: " Unless otherwise specified, a write operation will return only after writing all of the r requested bytes." Nice and clear. In contrast, read(ByteBuffer) says " A read operation might not fill the buffer, and in fact it might not read any bytes at all. Whether or not it does so depends upon the nature and state of the channel." Gee, thanks for clarifying. :roll:
----
Lastly, I would be remiss if I didn't note that FileChannel's API does say, nice and clearly, "File channels are safe for use by multiple concurrent threads." Which sure sounds reassuring. If we take this one statement at face value, great, no worries. But careful reading of the other statements made reveals potential problems.
"I'm not back." - Bill Harding, Twister
"I'm not back." - Bill Harding, Twister
"I'm not back." - Bill Harding, Twister
Originally posted by Jim Yingst:
[Jim]: It seems that if Sun really wanted to make a method that just does a read ASAP, they did a poor job of it.
[Max]: I'm not sure how you're drawing this conclusion. To say that it's not well documented is one thing: to say that it's badly implemented would require intimate knowledge of the code, which I lack.
Well I'm assuming that the implementation does obey the existing documentation. The documentation they provide needlessly prevents mutually concurrent reads, but allows reads concurrent with writes.
usable - gotta live with the warts. <frown>
Why one but not the other, if they were trying to make a fast read? Dunno what the implementation does here - if it obeys the doc, then it's the doc's fault the implementation is not as good as it could've been. Or if it ignores the docs here - well, I kinda approve in this case, because I think these particular docs need to be rewritten anyway. <smile> But in principal this is Wrong™; an implementation should obey its public API once that API has been officially released. As long as the API is at least [i]somewhat
[Max]: I think you mean slice in, not interrupt, unless I'm not following you @ all, which is possible.
What annoys me is when they claim to offer thread safety, and then leave these poorly-documented holes lying around.
[Max]: FileChannels are thread safe enough, in that you don't get data in an inconsistent state when you read or write.
The holes I've been discussing certainly do allow inconsistent data to be read. At least, as far as guarantees offered. If you read a record at the same time as you write it, without explicit synchronization, there's no guarantee that you won't read a mixture of new and old data. Maybe this will never actually happen - but I have no way of knowing that from the API.
[Max]:If you're an Object across several threads and you need thread safety, then you have to provide it yourself. If you don't need that safety, then you don't provide.
The important point with FileChannels is that reads and writes are atomic: thus, you wet call integrity between invocations to these methods. I thought that was your concern?
Umm, "wet call"? Is that a typo, or just a term I haven't heard before?
The problems I've been citing are directly involved with individual reads and writes, being performed by different threads on the same FileObject. That's been my concern all along. And one of my points has been that explicit-position reads and writes are not atomic - not when you mix reads and writes at least. And implicit-position methods may be atomic, but so what, if you can't supply the position in the same atomic method call, the atomicity doesn't extend far enough to really be useful. You get to atomically read or write to/from an unknown position. Whee, sounds like fun. Into the unknown we go... rolleyes
[Jim]: I suppose that there might be some ways to use the implicit-position methods in a multi-thread context without synchronization, if we never use position() at all, and we have multiple threads doing reads on "whatever is at the current position" and we don't care which thread reads what. There are some applications I can imagine where this might be useful. So OK, replace "pretty much useless" above with "useless for random access to a file".
[Max]: Make it 'useless if you require 100% clean reads', and it will be a fair statement. Otherwise, the implication is that only clean reads are useful, which is obviously (IMO), false.
Talking about the problems with implicit-position methods, the problem is for both reads and writes, because at the instant you invoke write() you don't know if some other thread has just repositioned the channel at the start of a completely different record. (Unless, again, you either synchronize or don't share FileChannel instances, which makes the talk of FileChannel's "thread safety" irrelevant - you provide it yourself.)
I acknowledge that occasional dirty reads may well be perfectly acceptable. (As long as you're aware of the possibility and don't ever use a potentially dirty read as the basis for an update.) Dirty writes would be a big problem however, and they're a distinct possibility if you do unsynchronized writes using implicit-position methods while other threads may be changing the position.
[Jim]: If you want to do a random-access read on a file without requiring synchronization, then read(ByteBuffer, long) is the one method that you want to be atomic. The other three read methods are no good anyway.
[Max]: Why? This only applies if you're doing random access read on a file, and keeping the connection open after the method's needs, and you're sharing the FileChannel across various threads, and you're demanding clean reads at all times. Relatively speaking, it's rare that I've found in this particular set of circumstances.
It was pretty common among people in this thread, except maybe for the "demanding clean reads at all times". That one may be just me. <smile>
And the idea of "sharing the FileChannel across multiple threads" was implicit in the API when they said "File channels are safe for use by multiple concurrent threads". If sharing FileChannels across threads is some rare exotic idea we're not supposed to consider, why did they bring it up? confused
And isn't "keeping the connection open after the method's needs" redundant here? We're sharing the FileChannel among threads. So yes, it would probably be a good idea if the channel wasn't closed at the end of each method invocation. That seems implicit in the whole "sharing a FileChannel" idea that you seem to dislike so much.
[Max]: However, in fairness, I think they(the Sun guys) did a pretty good job of implementing and documenting FileChannels.
Well, I haven't found any reason to complain about implementation. It's the documentation that leaves something to be desired, IMO. Certain parts at least. I'm not saying it's all bad, by any means. Maybe I've been spoiled by a lot of the improvements in Java over the years; I expect very high quality nowadays.
[Max]: As I understood it, you're locking on the recno, then you're locking on the FileChannel monitor, correct?
Regardless of the size of the second locking block, you're still nesting locks: one for the record, and another for the FileChannel. This is what makes me tremble
Yes, I'm nesting locks (currently); I just make sure you never call an outer-level lock from within an inner lock. Are you suggesting that deadlock is still possible under those circumstances? Or do you tremble at what junior programmers might do to the code later without understanding?
I can sympathize with the latter. <smile> If it's the former, could you elaborate on how deadlock is possible?
(Though to be fair, this part of the conversation is heading back towards SCJD, or Threads, so we may spawn yet another new topic if this is an involved issue in its own right.)
I'll also note that, given the fact that I sync on a Record object first, I don't really need the sync on the FileChannel as well, since I've already prevented concurrent access to the same record, and I'm not using implicit-position methods. So I may well do away with these nested locks as unnecessary. But they don't cause me to tremble, no. As you say elsewhere - that may simply be because I'm too stupid to be as afraid as I should be.
[ July 27, 2003: Message edited by: Jim Yingst ][/qb]
Originally posted by Jim Yingst:
Talking about partial reads and writes:
[Jim]: Taking the latter part first: I agree that it's possible read() isn't really susceptible to this problem at all. I haven't been able to demonstrate it in various tests I've run. But (a) I'm only testing on Windows 2000 Pro and XP Home at the moment, and (b) testing aside, the spec is still too vague.
[Max]: Do you mean the spec on the read(ByteBuffer, long), or other parts of the spec?
At this point, I was talking just about the spec for read(ByteBuffer, long). Though I do have gripes about other parts of the spec as detailed elsewhere... <smile>
[Max]: If the former, I'd have to agree by definition, since an experienced programmer like yourself finds it vague. However, I think I find it less vague then you do. That may simply be because I'm too stupid to be as afraid as I should be
Well, I wouldn't have said "stupid". Overconfident maybe. And if it's a platform-specific thing, then maybe it's not usually an issue for server-side development. If you're working on a specific server on a specific platform, and you test the heck out of your solution and don't run into any problems with partial reads, then great - it's probably not a problem on that platform. Or it's rare enough that it doesn't matter for your purposes.
[Max]: Um, I don't think you read that last part of that, which says.
a file channel cannot read any more bytes than remain in the file. It is guaranteed, however, that if a channel is in blocking mode and there is at least one byte remaining in the buffer then this method will block until at least one byte is read.
'Course I read it. (Whether I understood it as it was intended is another matter.) There are several problems I see with it. First, your quote omits the first part of the paragraph, which puts things in context a bit more:
[spec]: (1) A read operation might not fill the buffer, and in fact it might not read any bytes at all. (2) Whether or not it does so depends upon the nature and state of the channel. (3) A socket channel in non-blocking mode, for example, cannot read any more bytes than are immediately available from the socket's input buffer; similarly, a file channel cannot read any more bytes than remain in the file. (4) It is guaranteed, however, that if a channel is in blocking mode and there is at least one byte remaining in the buffer then this method will block until at least one byte is read.
I inserted the numbers in front of each sentence. When we look at the full quote, it's clearer that the last part of sentence (3) is not so strongly related to sentence (4). They're giving a number of examples of specific guarantees made by different types of channels; one of those is that a FileChannel can't read more bytes than are available in a file. Which is common sense really, and doesn't tell us anything about the minimum number of bytes read. OK, on to sentence (4), which is not necessarily about FileChannel specifically, but is talking about channels in general. We have:
It is guaranteed, however, that
Sounds good so far; this is the sort of thing I'm looking for.
if a channel is in blocking mode
Eh? Blocking mode?
This concept has been defined for SelectableChannels, which have nice isBlocking() and configureBlocking() methods to deal with this property. Unfortunately FileChannel isn't a SelectableChannel, so that's not relevant. The FileChannel docs do talk about certain methods blocking - except for the ones that don't. >rolleyes> Maybe we're supposed to magically assume that a FileChannel is in "blocking mode" based on this, but I don't really think so.
If they wanted to make any guarantees about whether FileChannels block or not, they could have done so (e.g. by making it implement SelectableChannel), but they didn't. I think they intentionally did not do this, probably because some existing native implementations do not block (just a guess, but seems reasonable) and they didn't want to make guarantees they couldn't back up. The issue of blocking generally isn't as important for a FileChannel as it is for a SocketChannel, as delays suffered waiting for a hard drive to read a file are much less significant than delays suffered while waiting for, say, a live user who's typing data to the other end of a socket. We really need nonblocking mode for some socket applcations; for FileChannels it's just "nice to have". But since this rule only talks about channels in blocking mode, it's already irrelevant to FileChannel. Continuing anyway though for the sake of argument, we have...
and there is at least one byte remaining in the buffer
OK, obviously we need space in the buffer to do a proper read. Let's say there are n bytes remaining in the file, and we've for a buffer with space for r bytes, where r >= n. Cool, that means we definitely have space for all the bytes we want...
then this method will block until at least one byte is read.
Um... OK. At least one byte will be read. Great. That gives us a lower limit. We also have two upper limits, r and n, and if r >= n we can say that no more than r bytes will be read. Great. So? Where is there any guarantee that r bytes actually are read? <confused>
Compare this to the API for WritableByteChannel's write(ByteBuffer) method, which says ever-so-clearly: "Unless otherwise specified, a write operation will return only after writing all of the r requested bytes." That is a useful guarantee, demonstrating that the java.nio folks do know how to write them if they feel like it. It's instructive to compare the text of the ReadableByeChannel and WritableByteChannel APIs. Many of the same sorts of statements are made, with parallel structures. It seems evident that either one was used as the basis for the other, or the two texts were developed concurrently. Either way, at least one Sun engineer seems to have looked at the text of both, and decided that it was appropriate for WritableByteChannel to have an explicit guarantee against partial writes (unless otherwise stated by a subclass) but not for ReadableByteChannel to have such a guarantee against partial reads. Unlike my gripes about vagueness in some other parts of the docs (blockage of FileChannel methods for example) this one feels to me like it's intentional - like they really meant to not give a guarantee against partial reads, perhaps because they knew it was possible on some platforms and they didn't have a good way to prevent it. If so, I wish they'd said it a bit more explicitly, rather than simply omitting a definitive statement about FileChannels. Clearly, it's too easy for people to misinterpret what they meant.
[ July 27, 2003: Message edited by: Jim Yingst ]
"I'm not back." - Bill Harding, Twister
Originally posted by Jim Yingst:
Hola, Max! We meet again.
[Max]: I think the difference is that I'm taking the API more literally then you are. You seem to be reading into it a bit, IMO.
<cough>
I was thinking rather the opposite...
ah, this may be part of the problem: I may be aware of a definition that you are not. All Channels are blocking, unless otherwise stated. Thus, by definition, a FileChannel is blocking, because it does not state otherwise.
Well it's certainly true I'm not aware of that. Got a reference?
The closest I can find is that the close() method of any Channel will block. But that's not the same as being a blocking Channel. For one thing, a nonblocking SelectableChannel will still have a close() that blocks. So close() has nothing to do with whether a Channel as a whole is considered "blocking" or not. Is there some other documentation that's relevant?
[Max]: I think your deconstruction is causing some of the confusion.
Amusing considering I first reconstructed a fuller version fo the quote to repair your own deconstruction. But OK, we'll look again...
[spec]: It is guaranteed, however, that if a channel is in blocking mode and there is at least one byte remaining in the buffer then this method will block until at least one byte is read.
[Max]: What they're saying is "even if there's only one byte left. Until your threads reads that one byte, it will not return'.
Well you added the "even if" yourself, and it (vaguely) implies more than was actually said here. But until the thread reads one byte, it won't return. Right.
if this is at least one byte then this method will block until at least one byte is read.. It's a strict if-then relationship here.
Fine, OK.
If there had been Q bytes, then the method would have blocked until at least Q bytes were read
No, it simply does not say that. You may want to replace "at least one" with "Q", but it's not justified.
A comparable read() version to give the guarantee we both wish were there, would be:
In ReadableByteChannel:
"Unless otherwise specified, a read operation will return only after reading all of the r requested bytes."
In FileChannel:
"A read operation will return only after reading all of the r requested bytes, unless fewer then r bytes remain in the channel. In the latter case the read will block until all remaining bytes in the channel have been read."
It wouldn't have been that difficult to say. No harder than what they did say about "at least one byte".
Incidentally, consider this quote from InputStream's read(byte[]) method:
"If len is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. If no byte is available because the stream is at end of file, the value -1 is returned; otherwise, at least one byte is read and stored into b."
Would you argue that this language implies that if Q bytes remain in the stream prior to end of file, and Q <= len, then Q bytes are stored into b? This seems like the same sort of situation as we've been discussing for FileChannel. Except that it's well-known (or if not, easily verifiable) that InputStreams, including FileInputStreams, do not necessarily fill a buffer with all bytes that may occur prior to end-of-stream. (Do we agree on that? I've befinitely observed incomplete reads from FileInputStreams, though not (yet) from FileChannel.) How does the FileChannel API somehow present a more convincing guarantee? I agree that in practice, for the limited platforms I've been able to test one, FileChannel seems much more immune to partial reads than FileInputStream is. And maybe it's always immune. But the API makes no such guarantee.
[Max]: I suspect that in light of the assertion that FileChannels are by definition blocking, your objections by and large are addressed?
Not yet, because (a) I haven't seen any documentation to back up your assertion, and (b) my latter objection about "at least one byte" is completely independent of that argument.
Cheers...
[ July 30, 2003: Message edited by: Jim Yingst ][/qb]
I promise I will be the best, most loyal friend ever! All for this tiny ad:
Gift giving made easy with the permaculture playing cards
https://coderanch.com/t/777758/Gift-giving-easy-permaculture-playing
|