Java’s Mysterious Interrupt
carlmastrangelo.com
carlmastrangelo.com
As a reminder, Thread.interrupt calls SocketChannel.close for any SocketChannel that is currently blocking.
Let's check out how SocketChannel is implemented on Android:
public synchronized int read(ByteBuffer target) ...
public synchronized void close() ...
Obviously, this causes a deadlock.The only alternative is using Thread.stop or completely creating your own SocketChannel implementation.
This was fixed in Android N in 2016, but few users have that update yet (less than a quarter)
This is one of the reasons why the process model is actually a much better approach.
Still, at least those "dangerous situations" are a bit more obvious/explicit when we're talking about processes rather than threads where everything is shared by default.
The people who write Google’s apps _have_ to be running into this stuff. But apparently either they’re not telling the standard lib people in a timely fashion, or they are but are being ignored.
The Android standard lib these days is OpenJDK-based, but bugs still happen there, too. Even the best of standard libs still have bugs.
https://android-review.googlesource.com/c/platform/bootable/...
Basically, when Android's libc was written they wrote `memset` wrong, such that it ignores the input parameter and always writes zeros. Nobody caught it for a while because, well, who calls `memset` with a non-zero value?
There was HN thread about it (8 years ago apparently, I feel old...):
https://news.ycombinator.com/item?id=1378912
The link itself is dead, but I managed to find the link to the code I posted above after a bit of searching.
For example, the result of "ab".split("") differs by android version. OpenJDK returns ["a", "b"], but older versions return ["", "a", "b"] and some even return ["", "a", "b", ""]. All this is strictly specified in the documentation and Java language specs, but no one ever bothered to fix it.
Android's libc is bionic and its ARM memset is here: https://android.googlesource.com/platform/bionic/+/master/li...
Edit: FWIW, I looked through the history, and it looks like Thread.destroy() was added by Bill Joy in Aug 1995. The initial implementation was "throw new NoSuchMethodError();".
Regardless, the deprecation serves as prominent warning that the programmer is about to shoot themselves in the foot.
(I'm actually more surprised that stop() hasn't been removed after all this time.)
Java 9 has introduced an API to get the process ID but before that, there was no defined way to get it, though it is accessible in a roundabout way via the JMX API, but even then it is not guaranteed. JNI would be another alternative.
It's strange to deprecate something when you can still achieve the same effect in some roundabout way?
Isn't that true for ALL APIs? You could always rewrite any deprecated API yourself, using sun.unsafe or JNI if it was needed, or even compile your own patched SDK fork.
The main intend behind deprecation is telling people "we don't want to support this anymore and you shouldn't be using it".
This is totally orthogonal to "you won't be able to replicate this behavior".
Few languages do cancellation well. Either it doesn't work right, or it does something overly elaborate like LISP breaks.
Sounds spooky, and it does require writing code in an exception-safe way, but that’s generally easy: pure code is largely exception-safe by nature, and impure code can use “bracket” (“finally”, “mask”, “ResourceT”, &c.) to acquire & release resources—and should already be using these functions anyway to handle regular synchronous exceptions. Async exceptions are very useful for killing a task that’s using too much time/memory, or initiating a graceful shutdown.
Go has its product types for errors, and Rust uses sum types.
As result you end up with basically the same code as with exceptions, just even more verbose.
Thats incorrect. Calling Thread#interrupted[0] clears the interrupt status and returns true if the current thread was interrupted. Thread#isInterrupted()[1] only returns the value of the interrupt status on the thread receiving the method call. The interrupt status is unaffected by that method call.
[0] https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.h...
[1] https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.h...