Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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):

  1. 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.
  2. 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.
  3. 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?

like image 750
Etki Avatar asked Apr 23 '16 08:04

Etki


2 Answers

Let me discuss each one of your available options.

  1. 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.

  1. 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.

  1. 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.

like image 120
MS Srikkanth Avatar answered Sep 28 '22 23:09

MS Srikkanth


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.

like image 26
Andy Turner Avatar answered Sep 28 '22 22:09

Andy Turner