• Post Reply Bookmark Topic Watch Topic
  • New Topic

A challenge for all of you

 
Omar IRAQI
Ranch Hand
Posts: 54
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello every body, I have a challenging problem for you.
Here it is :
Suppose that you have a DataInputStream object, and suppose that this object is reading bytes from an InputStream. Now what will you do if the reading process is blocked at the middle, (so the execution is stalled), knowing that the connection is not broken (so ther is no IOException), there will be no InterruptedException (no time out), and also no EOFException since the server has still data to send?
Also if you decide to fetch only the number of bytes that is returned by the available() method, you won't fetch the whole stream!!!
And to make it more challenging, I am seeking a solution that does not break the existing socket.
Good luck
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Create a separate thread which does a timed wait. If it times out, it should close the DataInputStream, which will cause the reading thread to throw an IOException, which you can then catch and decide what to do next. I think this does not close the socket - but you'll have to test to see. (Or study the docs more carefully than I have time for right now.)
How to detect the timeout in the first place? Well, the timer thread should have a reset() method which is accessible to the main I/O thread. All the reset needs to do is grab System.currentTimeMillis() and store it. The timer is constructed so that every time it comes out of a wait(), it checks the current time vs. the time of the last reset(). If this difference is too great, it times out and closes the stream. Otherwise it goes back to sleep until for a while. So the idea is, as long as some part of the program periodically hets reset(), the timer will not time out. It only times out if no one hits reset() within a certain time period.
OK, so how does the input stream reader hit reset? Construct a FilterInputStream whose function is simply to call reset() every time read() or reac(char[]) is called, and insert this filter into the chain of input streams you're reading from. For efficiency it's best if this filter is read using read(char[]) as much as possible - we don't need to het reset() for every byte read after all. Hitting it once for every group of bytes read will be sufficient.
Hope that makes sense. Enjoy...
 
Omar IRAQI
Ranch Hand
Posts: 54
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Jim,
Of course your solution does make sense, and you are close to the right solution, since you have thought of creating a separate thread that controlls the time out, and you have already the javax.swing.Timer that does it for you, you just need to implement its actionPerformed method to instruct what the Timer object (which runs in a different thread), should do at each tick.
Now, concerning closing the DataInputStream, it won't break the socket for sure, but I have tried it before, and the close() method was waiting too!!! : I think that the internal implementation of the close() method is synchronized, so it needs to lock the object that calls it.
But again you are very close to the right solution and the challenge is still open :=)
regards.
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hmmm... OK, I guess it makes sense that the close() method would hang too. But close() and read() internally will require synchronized access to the underlying stream, and if one method already has the lock, and hangs, then other method will never get the lock. Too bad; I guess that method won't work.
In e-mail you said that your solution was to "set the DataInputStream to null from the other thread, so it throws a
NullPointerException." But I can't see how this would work reliably between threads. Let's assume you use synchronized methods to allow both threads to access the input stream (otherwise, results will be unprdictable and inconsistent):
<code><pre>
public synchronized InputStream getInputStream() {
return input;
}

public synchronized void setInputStream(InputStream input) {
this.input = input;
}
</pre></code>
Thread 1 tries to read:
<code><pre>
char c = getInputStream().read();
</pre></code>
and thread 2 tries to stop this action:
<code><pre>
setInputStream(null);
</pre></code>
What happens will depend on which thread acts first - but the key point is that if thread 1 has already gotten the input stream and started executing read(), then it doesn't matter if thread 2 sets the input member variable to null - thread 1 doesn't need it anymore.
I was thinking about this problem more today. The timing part is no big deal. The real problem is, how to abort the blocked method? I was looking at a possible solution which avoids this issue by making use of the available() method so that it never calls read() for more bytes than are available without blocking. This can be done without creating a second thread at all. However, I can't find any way to detect the end of the stream in this case. When available() returns 0, you don't really know if this is because the stream is temporarily blocked, or because the underlying stream has ended. And there seems to be no method in the API that will tell us this information without risking a block. So instead we just loop, waiting for available() to return a nonzero value, or until the timeout occurs. Very annoying. I include my code below; maybe it will give someone an idea. I wrote this as a FilterInputStream extension so that it could be easily inserted into any InputStream chain - an equivalent FilterReader class would also be possible, with the same problems unfortunately.
<code><pre>
import java.io.*;

public class TimedInputStream extends FilterInputStream {

private long timeout; // time in milliseconds to wait before timing out
private long timerStarted; // system time as of last reset()

public TimedInputStream(InputStream in, long timeout) {
super(in);
this.timeout = timeout;
}

private void resetTimer() {
timerStarted = System.currentTimeMillis();
}

private long elapsedTime() {
return System.currentTimeMillis() - timerStarted;
}

public int read(byte[] buf, int offset, int length) throws IOException {
System.out.println("Excuting read(buf[" + buf.length + "], "
+ offset + ", " + length + ")");
resetTimer();
int available;
int totalRead = 0;
while (totalRead < length) {<br /> available = Math.min(available(), buf.length - offset);<br /> System.out.println(" available: " + available);<br /> if (available > 0) {
// good - we can read normally
int read = in.read(buf, offset, available);
totalRead += read;
resetTimer();
}
else {
// No bytes are currently available - either because stream is blocked,
// or because it's ended. Too bad we don't know which!
if (elapsedTime() > timeout) {
throw new IOException("Read timed out due to inactivity after "
+ elapsedTime() + " milliseconds");
}
Thread.yield(); // no need to try again immediately
}
}
return totalRead;
}

public static void main(String[] s) throws Exception {
BufferedReader reader = new BufferedReader(new InputStreamReader(
new TimedInputStream(new FileInputStream("C:\\Java\\sample.txt"), 2000)));
System.out.println(reader.readLine());
}
}
</pre></code>
If you run main(), you will see it reads the whole file, but times out after 2000 milliseconds sinc eit can't detect the end of the file. It throws an IOException when it times out rather than simply returning, which prevents the BufferedReader from ever returning from readLine(). Obviously we could return instead of throwing an exception (allowing the method to complete normally after a timeout) - but then we'd have no way of detecting actual problems in the stream. And it would be pretty inefficient to have to wait the whole timeout period before we could return, assuming ther ewas no problem in the input stream.
Incidentally, for a proper FilterInputStream we'd want to override some of the other methods as well, particularly the single-byte read() method, so that it does something similar. But since this doesn't seem to be a workable solution anyway, I'm omitting the additional methods. I think this solution is a dead end, but perhaps it will give someone else an idea. Sorry I don't see an answer so far - I'll let you know though if I think of anything else. Good luck.

[This message has been edited by Jim Yingst (edited July 16, 2001).]
 
Omar IRAQI
Ranch Hand
Posts: 54
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks jim, for your interest. As you see, this is a real problem!!!
So does any one have a suggestion for this?
 
Omar IRAQI
Ranch Hand
Posts: 54
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Some body in another site, suggested to me to use Socket.setSoTimeOut(long millis). It would have been very useful if I had a reference to the socket, but in my case I am using HttpURLConnection that hides the socket object. So, does this give to any one an idea?
[This message has been edited by Omar IRAQI (edited July 17, 2001).]
 
Yuri Gadow
Greenhorn
Posts: 28
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I believe Reinhold has solved your problem with java.nio.channels. If you can't wait, you could use Welsh's NBIO. As for trying to solve the problem in place, as it were, my first question is why are you using a high level abstraction (HttpURLConnection) when you need low-level control (DataInputStream and block handling)? I would suggest looking at an alternative method to handle your connections (i.e. directly.) I'm not saying there's not a solution to this, although it is a topic that has been beaten to death by many with no results that don't suck � but you may want to step back a bit and see if you can't get around having the problem in the first place.
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Cool - another new feature in 1.4 which I need to play with. Excellent. Thanks for pointing it out, Yuri.
I looked in Doug Lea's "Concurrent Programming in Java, Second Edition" and was amused to see that on p. 173, he commits basically the same error I did in the code above - he refuses to read unless avilable() returns > 0. Which will never happen at the end of a file, and so you time out. Glad to see I'm not the only one who fell into that trap.
Omar - if you can't use 1.4 beta (available now) and make use of Channels, another possibility is that you could use Thread stop() to put an end to a read() after it times out. I know, it's deprecated - but it is still implemented. I'm not sure exactly what trouble you might get into by using stop() in this case - but you can always try and see. Remember that any variables which were being accessed by the cancelled thread are now in a very indeterminate state, so don't trust their data at all. And the stream object which was doing the read() may or may not eventually manage to become garbage collected and close itself. Hopefully it won't be too much trouble if it stays open. It's an ugly solution, but it might work.
 
Sean MacLean
author
Ranch Hand
Posts: 621
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The non-blocking facilities in J2SE 1.4 are only useful for sockets (as far as I can figure anyway - since the non-blocking aspect comes from the selectable channel which only extended by the socket classes). Anyway, Welch's api is the only other solution I've found.
Sean
 
Omar IRAQI
Ranch Hand
Posts: 54
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for you all : Jim, Yuri, and Sean.
You have all given resonable suggestions, and I appreciate them.
Now I believe that I cannot abort the read() call, if I don't "at least" break the connection or stop the thread.
So, I am trying to disconnect (using another thread) from the HTTP server using HTTPURLConnection.disconnect(), and I think it works : I am still testing it...
---------------------------------
regards
 
Omar IRAQI
Ranch Hand
Posts: 54
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi guys,
I have end up by deciding to submit this problem as a bug to javasoft. And you know what ?, I have discovered that this bug is already listed and well known to javasoft since 1997 here is a reference to it : http://developer.java.sun.com/developer/bugParade/bugs/4389976.html
regards
[This message has been edited by Omar IRAQI (edited July 18, 2001).]
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!