Is Thread.interrupt() evil? - java

A teammate made the following claim:
"Thread.interrupt() is inherently broken, and should (almost) never be used".
I am trying to understand why this is the case.
Is it a known best practice never to use Thread.interrupt()? Can you provide evidence why it is broken / buggy, and should not be used for writing robust multithreaded code?
Note - I am not interested in this question if it's "pretty" from a design preservative. My question is - is it buggy?

Short version:
Is it a known best practice never to
use Thread.interrupt()?
No.
Can you provide
evidence why it is broken / buggie,
and should not be used for writing
robust multithreaded code?
The opposite is true: it is critical for multithreaded code.
See Listing 7.7 in Java Concurrency in Practice for an example.
Longer version:
Around here, we use this method in one specific place: handling InterruptedExceptions. That may seem a little strange but here's what it looks like in code:
try {
// Some code that might throw an InterruptedException.
// Using sleep as an example
Thread.sleep(10000);
} catch (InterruptedException ie) {
System.err.println("Interrupted in our long run. Stopping.");
Thread.currentThread().interrupt();
}
This does two things for us:
It avoids eating the interrupt exception. IDE auto-exception handlers always provide you with something like ie.printStackTrace(); and a jaunty "TODO: Something useful needs to go here!" comment.
It restores the interrupt status without forcing a checked exception on this method. If the method signature that you're implementing does not have a throws InterruptedException clause, this is your other option for propagating that interrupted status.
A commenter suggested that I should be using an unchecked exception "to force the thread to die." This is assuming that I have prior knowledge that killing the thread abruptly is the proper thing to do. I don't.
To quote Brian Goetz from JCIP on the page before the listing cited above:
A task should not assume anything about the interruption policy of its
executing thread unless it is explicitly designed to run within a
service that has a specific interruption policy.
For example, imagine that I did this:
} catch (InterruptedException ie) {
System.err.println("Interrupted in our long run. Stopping.");
// The following is very rude.
throw new RuntimeException("I think the thread should die immediately", ie);
}
I would be declaring that, regardless of other obligations of the rest of the call stack and associated state, this thread needs to die right now. I would be trying to sneak past all the other catch blocks and state clean-up code to get straight to thread death. Worse, I would have consumed the thread's interrupted status. Upstream logic would now have to deconstruct my exception to try to puzzle out whether there was a program logic error or whether I'm trying to hide a checked exception inside an obscuring wrapper.
For example, here's what everyone else on the team would immediately have to do:
try {
callBobsCode();
} catch (RuntimeException e) { // Because Bob is a jerk
if (e.getCause() instanceOf InterruptedException) {
// Man, what is that guy's problem?
interruptCleanlyAndPreserveState();
// Restoring the interrupt status
Thread.currentThread().interrupt();
}
}
The interrupted state is more important than any specific InterruptException. For a specific example why, see the javadoc for Thread.interrupt():
If this thread is blocked in an invocation of the wait(), wait(long),
or wait(long, int) methods of the Object class, or of the join(),
join(long), join(long, int), sleep(long), or sleep(long, int), methods
of this class, then its interrupt status will be cleared and it will
receive an InterruptedException.
As you can see, more than one InterruptedException could get created and handled as interrupt requests are processed but only if that interrupt status is preserved.

The only way I'm aware of in which Thread.interrupt() is broken is that it doesn't actually do what it seems like it might - it can only actually interrupt code that listens for it.
However, used properly, it seems to me like a good built-in mechanism for task management and cancellation.
I recommend Java Concurrency in Practice for more reading on the proper and safe use of it.

The main problem with Thread.interrupt() is that most programmers don't know about the hidden pitfalls and use it in the wrong way. For example, when you handle the interrupt, there are methods which clear the flag (so the status gets lost).
Also, the call will not always interrupt the thread right away. For example, when it hangs in some system routine, nothing will happen. In fact, if the thread doesn't check the flag and never calls a Java method which throws InterruptException, then interrupting it will have no effect whatsoever.

No, it's not buggy. It actually is the basis of how you stop threads in Java. It's used in the Executor framework from java.util.concurrent - see the implementation of java.util.concurrent.FutureTask.Sync.innerCancel.
As for failure, I've never seen it fail, and I've used it extensively.

One reason not mentioned is that the interrupt signal can be lost which makes invoking the Thread.interrupt() method meaningless. So unless your code in the Thread.run() method is spinning in a while loop the outcome of calling Thread.interrupt() is uncertain.

I noticed that when in thread ONE I execute DriverManager.getConnection() when there is no database connection available (say server is down thus finally this line throws SQLException) and from the thread TWO I explicitely call ONE.interrupt(), then both ONE.interrupted() and ONE.isInterrupted() return false even if placed as the first line in the catch{} block where SQLException is handled.
Of course I workarounded this issue implementing the extra semaphore but it is quite troublesome, as it is the very first such issue in my 15 years Java development.
I suppose it's because of bug in com.microsoft.sqlserver.jdbc.SQLServerDriver. And I investigated more to confirm that the call to the native function consumes this interruption in all cases it trhows its own, but preserves it when succeeded.
Tomek
P.S. I found the analogous issue.
P.P.S
I enclose a very short example of what I'm writting above. The registered class can be found in sqljdbc42.jar. I found this bug in classes built on 2015-08-20 then I updated to the newest version available (from 2017-01-12) and the bug still exists.
import java.sql.*;
public class TEST implements Runnable{
static{
try{
//register the proper driver
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
}
catch(ClassNotFoundException e){
System.err.println("Cannot load JDBC driver (not found in CLASSPATH).");
}
}
public void run() {
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().isInterrupted());
//prints true
try{
Connection conn = DriverManager.getConnection("jdbc:sqlserver://xxxx\\xxxx;databaseName=xxxx;integratedSecurity=true");
}
catch (SQLException e){
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().isInterrupted());
//prints false
System.exit(0);
}
public static void main(String[] args){
(new Thread(new TEST())).start();
}
}
If you pass something completely incorrect, as "foo", to the DriverManager.getConnection(), you will obtain the message "No suitable driver found for foo", and the second printout will be still true as one would expect. But if you pass the correctly built string but, say, your server is down or you lost your net connection (that can generally occurr in the production environment), you will see the java.net socket timeout error printout and the thread's interrupted() state is LOST.

THe problem is not that the implementation is not buggy but rather your thread is in an unknown state when it gets interrupted and this can lead to unpredictable behavior.

Related

Interrupted Exception Error in Java for Exception [duplicate]

What is the difference between the following ways of handling InterruptedException? What is the best way to do it?
try{
//...
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
OR
try{
//...
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
EDIT: I'd like to also know in which scenarios are these two used.
What is the difference between the following ways of handling InterruptedException? What is the best way to do it?
You've probably come to ask this question because you've called a method that throws InterruptedException.
First of all, you should see throws InterruptedException for what it is: A part of the method signature and a possible outcome of calling the method you're calling. So start by embracing the fact that an InterruptedException is a perfectly valid result of the method call.
Now, if the method you're calling throws such exception, what should your method do? You can figure out the answer by thinking about the following:
Does it make sense for the method you are implementing to throw an InterruptedException? Put differently, is an InterruptedException a sensible outcome when calling your method?
If yes, then throws InterruptedException should be part of your method signature, and you should let the exception propagate (i.e. don't catch it at all).
Example: Your method waits for a value from the network to finish the computation and return a result. If the blocking network call throws an InterruptedException your method can not finish computation in a normal way. You let the InterruptedException propagate.
int computeSum(Server server) throws InterruptedException {
// Any InterruptedException thrown below is propagated
int a = server.getValueA();
int b = server.getValueB();
return a + b;
}
If no, then you should not declare your method with throws InterruptedException and you should (must!) catch the exception. Now two things are important to keep in mind in this situation:
Someone interrupted your thread. That someone is probably eager to cancel the operation, terminate the program gracefully, or whatever. You should be polite to that someone and return from your method without further ado.
Even though your method can manage to produce a sensible return value in case of an InterruptedException the fact that the thread has been interrupted may still be of importance. In particular, the code that calls your method may be interested in whether an interruption occurred during execution of your method. You should therefore log the fact an interruption took place by setting the interrupted flag: Thread.currentThread().interrupt()
Example: The user has asked to print a sum of two values. Printing "Failed to compute sum" is acceptable if the sum can't be computed (and much better than letting the program crash with a stack trace due to an InterruptedException). In other words, it does not make sense to declare this method with throws InterruptedException.
void printSum(Server server) {
try {
int sum = computeSum(server);
System.out.println("Sum: " + sum);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // set interrupt flag
System.out.println("Failed to compute sum");
}
}
By now it should be clear that just doing throw new RuntimeException(e) is a bad idea. It isn't very polite to the caller. You could invent a new runtime exception but the root cause (someone wants the thread to stop execution) might get lost.
Other examples:
Implementing Runnable: As you may have discovered, the signature of Runnable.run does not allow for rethrowing InterruptedExceptions. Well, you signed up on implementing Runnable, which means that you signed up to deal with possible InterruptedExceptions. Either choose a different interface, such as Callable, or follow the second approach above.
Calling Thread.sleep: You're attempting to read a file and the spec says you should try 10 times with 1 second in between. You call Thread.sleep(1000). So, you need to deal with InterruptedException. For a method such as tryToReadFile it makes perfect sense to say, "If I'm interrupted, I can't complete my action of trying to read the file". In other words, it makes perfect sense for the method to throw InterruptedExceptions.
String tryToReadFile(File f) throws InterruptedException {
for (int i = 0; i < 10; i++) {
if (f.exists())
return readFile(f);
Thread.sleep(1000);
}
return null;
}
This post has been rewritten as an article here.
As it happens I was just reading about this this morning on my way to work in Java Concurrency In Practice by Brian Goetz. Basically he says you should do one of three things
Propagate the InterruptedException - Declare your method to throw the checked InterruptedException so that your caller has to deal with it.
Restore the Interrupt - Sometimes you cannot throw InterruptedException. In these cases you should catch the InterruptedException and restore the interrupt status by calling the interrupt() method on the currentThread so the code higher up the call stack can see that an interrupt was issued, and quickly return from the method. Note: this is only applicable when your method has "try" or "best effort" semantics, i. e. nothing critical would happen if the method doesn't accomplish its goal. For example, log() or sendMetric() may be such method, or boolean tryTransferMoney(), but not void transferMoney(). See here for more details.
Ignore the interruption within method, but restore the status upon exit - e. g. via Guava's Uninterruptibles. Uninterruptibles take over the boilerplate code like in the Noncancelable Task example in JCIP § 7.1.3.
What are you trying to do?
The InterruptedException is thrown when a thread is waiting or sleeping and another thread interrupts it using the interrupt method in class Thread. So if you catch this exception, it means that the thread has been interrupted. Usually there is no point in calling Thread.currentThread().interrupt(); again, unless you want to check the "interrupted" status of the thread from somewhere else.
Regarding your other option of throwing a RuntimeException, it does not seem a very wise thing to do (who will catch this? how will it be handled?) but it is difficult to tell more without additional information.
The correct default choice is add InterruptedException to your throws list. An Interrupt indicates that another thread wishes your thread to end. The reason for this request is not made evident and is entirely contextual, so if you don't have any additional knowledge you should assume it's just a friendly shutdown, and anything that avoids that shutdown is a non-friendly response.
Java will not randomly throw InterruptedException's, all advice will not affect your application but I have run into a case where developer's following the "swallow" strategy became very inconvenient. A team had developed a large set of tests and used Thread.Sleep a lot. Now we started to run the tests in our CI server, and sometimes due to defects in the code would get stuck into permanent waits. To make the situation worse, when attempting to cancel the CI job it never closed because the Thread.Interrupt that was intended to abort the test did not abort the job. We had to login to the box and manually kill the processes.
So long story short, if you simply throw the InterruptedException you are matching the default intent that your thread should end. If you can't add InterruptedException to your throw list, I'd wrap it in a RuntimeException.
There is a very rational argument to be made that InterruptedException should be a RuntimeException itself, since that would encourage a better "default" handling. It's not a RuntimeException only because the designers stuck to a categorical rule that a RuntimeException should represent an error in your code. Since an InterruptedException does not arise directly from an error in your code, it's not. But the reality is that often an InterruptedException arises because there is an error in your code, (i.e. endless loop, dead-lock), and the Interrupt is some other thread's method for dealing with that error.
If you know there is rational cleanup to be done, then do it. If you know a deeper cause for the Interrupt, you can take on more comprehensive handling.
So in summary your choices for handling should follow this list:
By default, add to throws.
If not allowed to add to throws, throw RuntimeException(e). (Best choice of multiple bad options)
Only when you know an explicit cause of the Interrupt, handle as desired. If your handling is local to your method, then reset interrupted by a call to Thread.currentThread().interrupt().
To me the key thing about this is: an InterruptedException is not anything going wrong, it is the thread doing what you told it to do. Therefore rethrowing it wrapped in a RuntimeException makes zero sense.
In many cases it makes sense to rethrow an exception wrapped in a RuntimeException when you say, I don't know what went wrong here and I can't do anything to fix it, I just want it to get out of the current processing flow and hit whatever application-wide exception handler I have so it can log it. That's not the case with an InterruptedException, it's just the thread responding to having interrupt() called on it, it's throwing the InterruptedException in order to help cancel the thread's processing in a timely way.
So propagate the InterruptedException, or eat it intelligently (meaning at a place where it will have accomplished what it was meant to do) and reset the interrupt flag. Note that the interrupt flag gets cleared when the InterruptedException gets thrown; the assumption the Jdk library developers make is that catching the exception amounts to handling it, so by default the flag is cleared.
So definitely the first way is better, the second posted example in the question is not useful unless you don't expect the thread to actually get interrupted, and interrupting it amounts to an error.
Here's an answer I wrote describing how interrupts work, with an example. You can see in the example code where it is using the InterruptedException to bail out of a while loop in the Runnable's run method.
I just wanted to add one last option to what most people and articles mention. As mR_fr0g has stated, it's important to handle the interrupt correctly either by:
Propagating the InterruptException
Restore Interrupt state on Thread
Or additionally:
Custom handling of Interrupt
There is nothing wrong with handling the interrupt in a custom way depending on your circumstances. As an interrupt is a request for termination, as opposed to a forceful command, it is perfectly valid to complete additional work to allow the application to handle the request gracefully. For example, if a Thread is Sleeping, waiting on IO or a hardware response, when it receives the Interrupt, then it is perfectly valid to gracefully close any connections before terminating the thread.
I highly recommend understanding the topic, but this article is a good source of information: http://www.ibm.com/developerworks/java/library/j-jtp05236/
I would say in some cases it's ok to do nothing. Probably not something you should be doing by default, but in case there should be no way for the interrupt to happen, I'm not sure what else to do (probably logging error, but that does not affect program flow).
One case would be in case you have a task (blocking) queue. In case you have a daemon Thread handling these tasks and you do not interrupt the Thread by yourself (to my knowledge the jvm does not interrupt daemon threads on jvm shutdown), I see no way for the interrupt to happen, and therefore it could be just ignored. (I do know that a daemon thread may be killed by the jvm at any time and therefore are unsuitable in some cases).
EDIT:
Another case might be guarded blocks, at least based on Oracle's tutorial at:
http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

Could I avoid interrupting Java thread again, when I know that it will terminate?

I have read all the related material around thread interruptions in Java and their contribution as a mechanism to support gracefully cancellable tasks. However, I have a very specific case, where I think it makes sense (against all suggestions) to neither re-throw the InterruptedException nor set the interrupted status.
So, let's say I am implementing a retrying mechanism over a method. Let's say the method is:
...
public String call(String request) {
return "Hello " + request;
}
...
I am implementing the retrying, by wrapping this method with another method, that catches any Exception thrown by this method, sleeping for a specific interval and then retrying. The related code is:
public String wrapperCall(String request) {
...
try {
call(request);
} catch(Exception exception){
try {
Thread.sleep(100);
} catch(InterruptedException e) {
throw new MaxRetryException("Stopped retrying due to interruption");
}
}
...
}
I believe that in this case I can avoid both re-throwing the InterruptedException or setting the interrupted status on the thread, because:
The call will fail with the MaxRetryException, which is a RuntimeException anyway, so this thread will terminate. The call of this method will be done as part of a web server request, so there is no reason to think about cancellability.
There is no ExecutorService that will need to be informed, so that it perform cancelling processes or resource cleaning
Obviously, I know that ideally I should follow the suggested principle to be covered against all possible uses of this method. However, for other practical reasons, I cannot do it. So, I am trying to understand what are the risks of not doing it.
Am I right in thinking that this case is an exception, where avoiding propagating the interrupted status upwards will not create any issue ?
UPDATE 1: To provide some more context, the reason I could not set the interrupt status was the failure of Jacoco to generate some reports. This seems to be due to a JDK bug. I finally managed to set the interrupt status and avoid the Jacoco failure, by resetting the interrupt status in all the relevant unit tests. However, the question still remains if there is a need for the interrupt to be set in this context and who could benefit from it, besides just setting it for the sake of good order.

How does one correctly handle InterruptedException that may not be passed to client code?

I've stumbled upon a situation where i have to deal with InterruptedException, but can't pass it upwards and don't feel that my code should be allowed to swallow it. To be precise, i'm working on a distributed lock implementation, and request to backing service may be interrupted or even time out - and, of course, java.util.concurrent.Lock doesn't account for such cases and doesn't allow me to spit out InterruptedException. I'm struggling to write correct implementation for non-throwing lock(), tryLock() and unlock() methods.
So, the question is - what would be correct strategy to handle case like this? From current point of view i see only three options (and i feel smell for every of them):
Ignore interrupted exception in lock / tryLock / unlock methods, retrying / returning false / assuming that even if request hasn't got to it's destination, TTL will eventually unlock record. This is obviously not the best solution because it hopes that everything will be good instead of dealing with problems.
Wrap in RuntimeException heir. This seems to be awful solution as well, since client code will have to work with concrete implementation rather than original interface, and unchecked exception certainly were not made for purpose like that.
Use the force Thread.currentThread().interrupt() call. I don't like this way because it basically tells thread to process it's own interrupt rather than pass a notice about call being interrupted; also, as far as i understand, if there's no outside polling, it will make thread eventually, but not instantly process interrupt, probably, in completely another place.
(And, of course there's an option to allow client code configure desired behavior, but that still doesn't provide me with a really good solution)
Is there any better way than any i've described? And if no, which one should be preferred over others?
Let me discuss each one of your available options.
Ignore interrupted exception
This is wrong. It is never right to swallow the exception when you are implementing something like a library which other users will come to rely upon. In these cases, it would never be prudent to swallow an exception unless you propagate it as a different exception which provides more meaningful information to the client. An InterruptedException is basically a request to cancel your thread and this information should never be suppressed from the client irrespective of whether the lock would be unlocked later. The client needs to know that someone wants the unit of work being carried out by this thread to be stopped.
Wrap in RuntimeException
No. This is wrong as well for exactly the same reason as above. the reason for propagating an InterruptedException is to let a client know that a request has been made to cancel an executing thread and hence wrapping it in a RuntimeException is wrong because this information is lost.
Use/force Thread.currentThread().interrupt() call
This may be right or wrong depending on the use case. Ask yourself if it would be ok for you to propagate the InterruptedException.
If it is ok to do so (it is not in your case but), then you can declare that your method throws InterruptedException and let the callers above worry about what needs to be done. This would typically be the case when you make a call to a method (say operation()) that throws an InterruptedException and you won't be able to proceed further unless this call completes. Suppose operation() throws InterruptedException then there is nothing much you can do other than propagating this exception. So you shouldn't catch the exception. In this case just declare that your method throws InterruptedException and you are done
If it is not ok to do so then the correct way to handle it would be to force an interrupt() call. Using this you suppress the exception but you still give the client the option of checking the flag to see if an interruption request was made. And you are right. This requires the client side to poll rather than processing the interruption. But this is not wrong. If you don't want clients to poll then propagating the exception would have been the better option. But this is not always possible and your example is one such use case. And there are many cases where a thread of execution can return some meaningful information even when it is Interrupted. So in this cases the exception is suppressed but the information that there was a request for termination can still be passed above by calling interrupt() method. So the client can either just use the result that was returned from a partial computation or poll to check if the interrupt flag was set depending on the use case. So you are giving the client more flexibility by doing this.
For me, the answer is almost always 3.
Java uses a cooperative interruption model: it feels like a very British approach to me:
I say, old chap, would you mind stopping what you are doing, if it is not too much trouble?
But there is no compunction to act upon the interruption in a timely way (or, indeed, at all). To use a Robin Williams quote:
Stop! ...or... I'll say stop again!
You can write your code to check for interruptions periodically, or not - it'd get very messy and repetitive if you did it everywhere. But, if you don't want to do anything when you are interrupted, you should at least preserve the fact that an interruption did occur, in order that calling code which does want to do something to act accordingly.
There is nothing really special about InterruptedException - it is literally an empty subclass of Exception. It is only typically only thrown in the first place if a particular method checks Thread.interrupted() or .isInterrupted(). So, I wouldn't worry about the fact that calling interrupt() doesn't immediately cause the thread to stop what it is doing - that is the very nature of cooperative interruption.
To qualify why I say "almost always" above: the Java tutorial describes interruption thus:
An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate.
I have only very, very rarely done anything other than wanting to terminate an interrupted thread.
If I wanted a thread "to do something else", I would likely be using an executor service anyway: each of the "things" to be done is represented by a separate Runnable, so I don't even know if they are done in the same thread anyway.
So I would typically interrupt the thread, and then throw a RuntimeException:
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
Each Runnable just finishes what it is doing when interrupted; the executor service decided whether or not to do another task.
It is basically only if writing framework-level code (like an ExecutorService) that I would choose to continue after interruption.
It totally depends on your application, in particular on the meaning of an InterruptedException in your thread.
Option 1 for me is a bad practice: relying on other mechanisms make your code not clear and scary. You can always catch an exception and use only the finally method to release all the locked resources.
Then relaunching the exception or hiding it (probably returning a specific result) is up to you and the specific meaning that that exception has in your application.

Is this appropriate use of Thread.interrupt()?

I want a piece of code to run as long as the thread isn't interrupted. Currently what I'm doing is this:
while(!Thread.interrupted()){
// .. some code
try {
Thread.sleep(4000);
} catch(InterruptedException ex){
break;
}
// .. some more code
}
My question is: is this good practice? Is it appropriate use of interrupt?
Yes, I think this is the very purpose of interrupt(), so it seems like a legitimate usage.
Conceptually this is on the right track. There are some details that could be improved, though.
What's right about this is that the thread interruption mechanism is being used to tell the thread to do something else, and the thread can respond how it pleases at a time of its choosing. In this case, catching InterruptedException from Thread.sleep() and breaking out of the loop is an entirely reasonable thing to do. It's certainly much preferable to what is usually done, which is to ignore the exception entirely. (This is usually wrong.)
What might be an issue is that the loop condition also checks whether the thread has been interrupted. This might be a problem, depending on what some code and some more code are doing. Your system might be left in different states depending on which chunks of code have been executed when the interrupt is detected.
Unless one iteration of your loop runs for a really long time (aside from the sleep), it's usually only necessary for there to be a single interruption point in a loop. In this case, if the thread is interrupted while it's doing some code processing, an InterruptedException will be generated immediately upon the call to Thread.sleep(). So you could just change the loop condition to while (true) and have the catch-block break out of the loop. (If you do this, you should also reassert the interrupt bit; see below.)
Alternatively, if you only want to check for interrupts at the top of the loop, you could do this:
while (!Thread.interrupted()) {
// .. some code
try {
Thread.sleep(4000L);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
// .. some more code
}
This arranges there to be only a single exit point from the loop, which might makes the code easier to reason about.
Note the technique used here is to reassert the interrupt bit on the thread after having caught InterruptedException. The general rule about handling interrupts is that either the interrupt bit should be reasserted, or an InterruptedException should be propagated. If neither is done, then the calling code might end up blocked in a subsequent wait() call, with no knowledge that it had ever been interrupted. That's usually an error.
Good practice is to use the newer higher level APIs such as an ExecutorService instead. You shouldn't use the lower level APIs such as threads or synchronize or wait/notify.

Handling InterruptedException while waiting for an exit signal (bug in Android?)

I've come across the code below, and I'm wondering if it does exactly what I think it does:
synchronized(sObject) {
mShouldExit = true;
sObject.notifyAll()
while (!mExited) {
try {
sObject.wait();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
About the context: there is another thread that checks for mShouldExit (inside the sObject monitor) and will exit in that case.
This does not look to be a correct pattern to me. If an interrupt happens, it will set the interrupted status again, so when it returns to sObject.wait(), another InterruptedException will come etc. etc. etc. Therefore, it can never go to truly waiting state (sObject.wait()) i.e. it will never release the sObject monitor. This may result in an infinite loop, as the other thread cannot set mExiting to true, because it can never enter sObject's monitor. (So I think that the interrupt() call is an error, it must not be used here.) Am I missing something?
Note that the code snippet is a part of the official Android framework source code.
UPDATE: actually, the situation is worse, because the same pattern is used in Android when your GL rendering starts. The official source code of GLSurfaceView.GLThread.surfaceCreated():
public void surfaceCreated() {
synchronized(sGLThreadManager) {
if (LOG_THREADS) {
Log.i("GLThread", "surfaceCreated tid=" + getId());
}
mHasSurface = true;
sGLThreadManager.notifyAll();
while((mWaitingForSurface) && (!mExited)) {
try {
sGLThreadManager.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
You can reproduce the bug in a similar way: make sure your UI thread has its interrupted status flag yet, then add your GLSurfaceView and start the GL rendering (via setRenderer(...), but on some devices, make sure your GLSurfaceView has Visibility.VISIBLE status, otherwise rendering will not start).
If you follow the above steps, your UI thread will end up in an infinite loop, because the above-quoted code will keep generating an InterruptedException (due to wait()) and therefore the GL thread will never be able to set mWaitingForSurface to false.
According to my tests, it seems that such an infinite loop will also result in an endless sequence of GC_CONCURRENT garbage collection (or, at least, such messages in logcat). Interesting, someone had an unknown poorly-defined issue on stackoverflow earlier which might be related:
How to solve GC_concurrent freed?
Isn't it possible that perhaps his UI thread had its interrupted flag set to true, and he was using a GLSurfaceView for the map he mentions? Just an assumption, a possible scenario.
Short version: That code is wrong, and will cause an infinite loop (I still have a doubt, but may depend on JVM implementations). Setting the interrupt status is the right thing to do, but it should then exit the loop, eventually checking that same interruption status using Thread.isInterrupted().
Long version for the casual reader:
The problem is how to stop a thread that is currently executing some work, in response to a "Cancel" button from the user or because of some other application logic.
Initially, Java supported a "stop" method, that preemptively stopped a thread. This method has been demonstrated to be unsafe, cause didn't give the stopped thread any (easy) way to clean up, release resources, avoid exposing partially modified objects and so on.
So, Java evolved to a "cooperative" Thread "interruption" system. This system is quite simple : a Thread is running, someone else calls "interrupt" on it, a flag is set on the Thread, it's Thread responsibility to check if it has been interrupted or not and act accordingly.
So, correct Thread.run (or Runnable.run, of Callable etc..) method implementation should be something like :
public void run() {
while (!Thread.getCurrentThread().isInterrupted()) {
// Do your work here
// Eventually check isInterrupted again before long running computations
}
// clean up and return
}
This is fine as long as all the code your Thread is executing is inside your run method, and you never call something that blocks for a long time ... which is often not the case, cause if you spawn a Thread is because you have something long to do.
The simplest method that block is Thread.sleep(millis), it's actually the only thing it does : it blocks the thread for the given amount of time.
Now, if the interrupt arrives while your thread is inside Thread.sleep(600000000), without any other suport, it would take a lot for it to arrive to the point where it checks isInterrupted.
There are even situations where your thread would never exit. For example, your thread is computing something and sending results to a BlockingQueue with a limited size, you call queue.put(myresult), it will block until the consumer free some space in the queue, if in the mean time the consumer has been interrupted (or died or whatever), that space will never arrive, the method will not return, the check on .isInterrupted will never be performed, your thread is stuck.
To avoid this situation, all (most) methods that interrupt the thread (should) throw InterruptedException. That exception simply tells you "I was waiting for this and that, but in the meanwhile the thread as been interrupted, you should do cleanup and exit as soon as possible".
As with all exceptions, unless you know what to do, you should re-throw it and hope that someone above you in the call stack knows.
InterruptedExceptions are even worse, since when they are thrown the "interrupted status" is cleared. This means that simply catching and ignoring them will result in a thread that usually does not stop :
public void run() {
while (!Thread.getCurrentThread().isInterrupted()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Nothing here
}
}
}
In this example, if the interrupt arrives during the sleep() method (which is 99.9999999999% of the time), it will throw InterruptedException, clear the interrupt flag, then the loop will continue since the interrupt flag is false, and the thread will not stop.
That's why if you implement your "while" correctly, using .isInterrupted, and you really need to catch InterruptedException, and you don't have anything special (like cleanup, return etc..) to do with it, least that you can do is set the interrupt flag again.
The problem in the code you posted is that the "while" relies solely on mExited to decide when to stop, and not ALSO on isInterrupted.
while (!mExited && !Thread.getCurrentThread().isInterrupted()) {
Or it could exit when interrupted :
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return; // supposing there is no cleanup or other stuff to be done
}
Setting the isInterrupted flag back to true is also important if you don't control the Thread. For example, if you are in a runnable which is being executed in a thread pool of some kind, or inside any method anywhere you don't own and control the thread (a simple case : a servlet), you don't know if the interruption is for "you" (in the servlet case, the client closed the connection and the container is trying to stop you to free the thread for other requests) or if it's targeted at the thread (or system) as a whole (the container is shutting down, stopping everything).
In that situation (which is 99% of the code), if you cannot rethrow the InterruptedException (which is, unfortunately, checked), the only way to propagate up the stack to the thread pool that the thread has been interrupted, is setting the flag back to true before returning.
That way, it will propagate up the stack, eventually generating more InterruptedException's, up to the thread owner (be it the jvm itself, of an Executor, or any other thread pool) that can react properly (reuse the thread, let it die, System.exit(1) ...)
Most of this is covered in chapter 7 of Java Concurrency in Practice, a very good book that I recommend to anyone interested in computer programming in general, not just Java, cause the problems and the solutions are similar in many other environments, and explanations are very well written.
Why Sun decided to make InterruptedException checked, when most documentation suggests to rethrow it mercilessly, and why they decided to clear the interrupted flag when throwing that exception, when the proper thing to do is setting it to true again most of the time, remains open for debate.
However, if .wait releases the lock BEFORE checking for the interrupt flag, it open a small door from another thread to modify the mExited boolean. Unfortunately the wait() method is native, so source of that specific JVM should be inspected. This does not change the fact that the code you posted is coded poorly.

Categories

Resources