5 ms·
Neat to see sleep calls artificially introduced to reliably recreate the deadlock. [0] Looks like fixing the underlying bug is still in-progress, [1] I wonder
by MaxBarraclough 1y ago
Neat to see sleep calls artificially introduced to reliably recreate the deadlock. [0]
Looks like fixing the underlying bug is still in-progress, [1] I wonder how many lines of code it will take.
[0] https://github.com/aoli-al/jdk/commit/625420ba82d2b0ebac24d9954e09062e3fbc0378 https://github.com/aoli-al/jdk/commit/625420ba82d2b0ebac24d9...
[1] https://bugs.openjdk.org/browse/JDK-8358601 https://bugs.openjdk.org/browse/JDK-8358601
- trhway 1y agowithout reworking of the code all these checks of the executor and queue state and queue manipulations have to be under a mutex, and that is just a few lines.
- brabel 1y agoBugs like these are pervasive in languages like Java that give no protection against even the most basic race condition causes. It’s nearly impossible to write reliable concurrent code. Freya only helps if you actually use it to test everything which is not realistic. I am convinced, after my last year long struggle to get a highly concurrent Java (actually Kotlin but Kotlin does not add much to help) module at work, that we should only use languages that provide safe concurrency models, like Erlang/Elixir and Rust, or actor-like like Dart and JavaScript, where concurrency is required.
- gf000 1y agoWhat is a safe concurrency model? Like, actors can trivially deadlock/livelock, they are no panacea at all, and are trivial to recreate (there are a million java implementations) You make it sound like there is some modern development superseding what java has, but that's absolutely not the case. Like even rust is just pretty much a no-overhead `synchronized` on top of an object. It is necessary there, because data races are a fundamental memory safety issue, but Java is immune to that (it has "safe" data races). Logical bugs can trivially happen in either case - as an easy example even if all your fields are atomically mutated, the whole object may not make sense in certain states, like a date with February the 31st. Rust does nothing against such, and concurrent data structures have ample grounds for realistic examples of the above.
- mrkeen 1y ago> What is a safe concurrency model? STM. The terms 'atomic', 'thread-safe', and 'concurrent' collections are thrown around too loosely for application programmers IMO, for exactly your example above. In other scenarios, 'atomics' refer to the ability to do one thing atomically. With STM, you can do two or more things atomically. Likewise with 'thread-safe'. Thread-safe seems to indicate that the object won't break internally in the presence of multiple threads, which is too low of a bar to clear if your goal is to write an actually thread-safe application out of so-called 'thread-safe' parts. STM has actual concurrent data structures, where you can write straight-line code like 'if this collection has at least 5 elements, then pop one'. I don't think the Feb 31 example is that fair though, because if you want to construct a representation of Feb 31, who's going to stop you? And if you don't want to, plain old static types is the solution.
- gf000 1y agoI couldn't give a better reply than this author: https://joeduffyblog.com/2010/01/03/a-brief-retrospective-on-transactional-memory/ https://joeduffyblog.com/2010/01/03/a-brief-retrospective-on... Also, a phenomenal writing (as are his other posts) on the whole concurrency landscape, see: > A wondrous property of concurrent programming is the sheer number and diversity of programming models developed over the years. Actors, message-passing, data parallel, auto-vectorization, …; the titles roll off the tongue, and yet none dominates and pervades. In fact, concurrent programming is a multi-dimensional space with a vast number of worthy points along its many axes.
- mrkeen 1y agoI've read a few postmortems about STM. I have to take them with a grain of salt because I usually read those reports right after doing a bunch of STM programming, and right before doing a bunch more STM programming. Reports of its death have been greatly exaggerated. Here it is in 2006 featuring the same Tim from your article: https://www.youtube.com/watch?v=tve57vilywc https://www.youtube.com/watch?v=tve57vilywc I didn't start using it in anger till 2013-2014 maybe? But I don't recall any major differences between what the video shows and how it works in 2025. Anyway, postmortems usually boil down to two issues: 1) That's not how programmers usually do it 2) We couldn't pull it off The most obvious explanation for 1 is 2. I, too, would be disappointed by the low-adoption rates of my new technology if I hadn't built it or released it to users. But the article has some gems: Transactions unfortunately do not address one other issue, which turns out to be the most fundamental of all: sharing. Indeed, TM is insufficient – indeed, even dangerous – on its own because it makes it very easy to share data and access it from multiple threads; I cannot read this charitably. This is the only reason for, not a damning reason against. It's like doing research & development on condoms, and then realising it's a hopeless failure because they might be used for dangerous activities like sex. I already mentioned a great virtue of transactions is their ability to nest. But I neglected to say how this works. And in fact when we began, we only recognized one form of nesting. You’re in one atomic block and then enter into another one. What happens if that inner transaction commits or rolls back, before the fate of the outer transaction is known You nest transactional statements, not the calls to atomic. The happy-path for an atomic is that it will commit; it should be obvious a priori that something that commits cannot be in the codepath that can be rolled back. Then that same intern’s casual statement pointing out an Earth-shattering flaw that would threaten the kind of TM we (and most of the industry at the time) were building. ... An update in-place system will allow that transaction to freely change the state of x. Of course, it will roll back here, because isItOwned changed to true. But by then it is too late: the other thread using x outside of a transaction will see constantly changing state – torn reads even – and who knows what will happen from there. A known flaw in any weakly atomic, update in-place TM. If this example appears contrived, it’s not. It shows up in many circumstances. I agree that it's not contrived. It's in the problem-space of application writers. It's not a problem caused by introducing STM. We want an STM system to allow safe access to isItOwned & x, because it's a PITA to try to do this with locks.
- rand_r 1y agoRace conditions are generally solved with algorithms, not the language. For example, defining a total ordering on locks and only acquiring locks in that order to prevent deadlock. I guess there there are language features like co-routines/co-operative multi-tasking that make certain algorithms possible, but nothing about Java prevents implementing sound concurrency algorithms in general.
- mrkeen 1y ago> Race conditions are generally solved with algorithms, not the language. For example, defining a total ordering on locks You wouldn't make that claim if your language didn't have locks.
- brabel 1y agoExactly, this thread is full of ignorant comments. I was talking about a certain class of race conditions that can be completely prevented in some languages, like Rust (through its aliasing rules that just make it impossible to mutate things from different threads simultaneously, among other things) and languages like Pony, for example, as the language uses the Actor model for concurrency, which means it has no locks at all (it doesn't need them), though I mentioned Dart because Dart Isolates look a lot like Actors (they are single-threaded but can send messages and receive messages from other "actors", similarly to JS workers).
- gf000 1y agoIn Java, racing a field is safe - you can only ever observe the value as one that was explicitly set by a thread, no tearing can happen. Safe data races can happen in Java, but you sometimes do want that (e.g. efficient concurrent algorithms), and avoiding it is not particularly hard (synchronized blocks are not the state of the art, but does make it easy to solve a problem). Pony and Rust are both very interesting languages, but it is absolutely trivial to re-introduce locks with actors, even just accidentally, and then you are back at square 1. This is what you have to understand, their fundamental model has a one-to-one mapping to "traditional" multi-threading with locks. The same way you can't avoid the Turing model's gotchas, actors and stuff won't fundamentally change the landscape either.