Feb 25 2008
Headius and Ruby Threading: Software Reinvention isn’t Sexy, It’s Stupid
Check out this article by Headius. He’s discovered that Ruby adopted some of the same broken threading structures that early Java did. And, lo! and behold that they are broken in the same way as Java was.
*headdesk*
This gets me to a point that Brian and I regularly gripe about on the phone: why is it that the software development industry, which has all these awesome ways of sharing what has previously been done, constantly finds itself reinventing the wheel? What’s even better is that these reinventors usually take a look at the wheel and go, “Y’know, this whole round wheel thing is great, but I’m going to make it a hexagon this time, so that we can get added stability.” There is then a huge amount of hype about how great the hexagonal wheel is, how you — yes, you! — could make millions of dollars by founding a hexagonal wheel start-up, and before too long there’s widespread industry adoption that will take major effort to reverse. And then, years later, people still don’t get why the hexagonal wheel is broke. The only good news about all of this is that it gives lots of fodder for bloggers to come in and constructively gripe…although I suppose that statement depends on your definition of “good news”.
Sometimes, there are revolutionary steps forward in software — major changes to the very way we think about development. Hype is not all bad. Conventional development (popularized by Rails) was one of these steps. However, if you’re setting out to build a library or a framework or (especially) a language, you should be building off what already exists, not re-inventing the wheel (and making it hexagonal this time).
Popularity: 10% [?]
What I find most humorous is the giant elephant he swept under the rug with the statement:
We could also assume a perfectly spherical cow. My experience has been that data corruption is a much more common case- the race condition he pointed out is a very real one, and definitely a problem, but now you have the case that any code, any where, can throw any exception at any time- not because the code is throwing the exception, but because the exception is caused by some external force (some other thread calling Thread#raise). Every function, in every library and every class, every where, now has to be able to handle some other thread calling Thread#raise at any point- no code is safe. You might think code like:
mutex#lock();payer_account#debit();payee_account#credit();mutex#unlock();</CODEis perfectly safe, especially if the credit and debit functions don’t throw exceptions. But it isn’t. Because some other thread might decide to call Thread#raise() on you in between the debit and the credit statements.
There is one time when reinventing the wheel is justified- and that’s when everyone else is coping with their hexagonal, square, and even triangular wheels, and you want a round one. What this analogy misses is that most of the time what this really calls for is a change in paradigm, a change in the way of thinking. A round wheel is, in some sense, a infinitely-sided polygon wheel, and thus is a natural outgrowth of the movement from triangular, to square, to hexagonal wheels. As the comments to the original post mentioned, the behavioir being given is one that’s needed. But the solution isn’t going to be as simple as just fixing (somehow) Thread#raise.
One thing you are correct about. When you go to reinvent the wheel, the one thing you absolutely must have is a working knowledge of the wheels that have been invented before- and a knowledge of both why they succeeded and why the failed. And a story as to why your wheel will succeed where others failed, and also succeed where the others succeeded. It’s not the tendency to reinvent the wheel that drives me batshit insane, it’s that the computer industry keeps reinventing the same wheels over and over again, because they don’t learn from others mistakes (often they don’t learn from their own, it seems).
Ruby and Java were originally developed at around the same time (mid-1990’s). While I don’t have the timetables under which each language developed their original cut of threading primitives, it’s well within reason they were developed around the same time as well. Unless you have concrete proof that Ruby’s threading primitives were developed after Java’s were demonstrated as being faulty, then you can’t really “headdesk” about Ruby not learning from Java’s mistakes.
That being said, Ruby does need a clearer API deprecation mechanism and does need to deprecate those APIs, like Java did. Even better, of course, is to work out the patterns for safer multi-threading and pour them into the language (e.g., actors).
This is true. It’s possible that Ruby and Java developed them at the same time.
However, Java — the epically stagnant and tragically unhip enterprisey language — knocked those methods out in 1.2, which was released a decade ago.
Are you saying that I shouldn’t expect Ruby to have caught up?
“Are you saying that I shouldn’t expect Ruby to have caught up?”
No, I’m saying you need to read your own writing, then figure out what you want to bitch about, and let us know when you’re back in sync with yourself.
The crux of your rant is:
“why is it that the software development industry, which has all these awesome ways of sharing what has previously been done, constantly finds itself reinventing the wheel?”
You have shown no evidence that Ruby reinvented Java’s threading implementation. Hence, you have no evidence of that of which you are “*headdesk*”-ing, and that is what you pinned your entire rant upon.
Now, if you want to write a blog post about why this problem has existed in Ruby a decade after Java deprecated its similar APIs, that’s cool. Or, if you want to come up with another example that’s more accurate, that’s cool.
But complaining that developers are “reinventing the wheel” makes no sense given your example, which does not demonstrate “reinventing the wheel”.
@Mark: Perhaps “reinventing the wheel” isn’t a perfect fit as an expression, but the spirit of not learning the lessons of your predecessors and/or contemporaries is certainly there.
@Mark
You’re right — this particular example isn’t the greatest to illustrate the general principle I was getting at. It just blew me away that this wasn’t something that Ruby was aware of, since it was part of my Concurrency 101 experience in the Java/C# world.
“It just blew me away that this wasn’t something that Ruby was aware of, since it was part of my Concurrency 101 experience in the Java/C# world.”
I’m right with you there. At minimum, they should have used ‘warn’ to emit a warning and used that as their deprecation-notification mechanism.
FWIW, threading in general is getting a major overhaul, both in “classic” Ruby (MRI/YARV) and in alternative Ruby engines (JRuby, Rubinius, and presumably IronRuby). So I doubt this will remain an issue for terribly long.
I’ve seen this thing done in correct way in Delphi 3 around 1997 where class TThread had property named Terminated which sole purpose was to allow you to inform thread that it should end. There was no method to stop thread from outside. You just set Terminated to true and your own code inside thread must examine this property and end if it is set.
Where are current proposals heading for the major overhaul? Imperative threading models are just riddled with issues, and we keep going back and forth between user-space locking and syntactic locking, both of which suck in their own distinct ways. Ruby already offers both of those approaches, so I’m not sure where they’re heading next.
If they’ve got something wild coming out, I’d love to see it.
[...] I’m still cranking away on Java CONcurrency Handler, although I’m losing steam. The binary and source distributions have a whopping 18 downloads between them, and aside from Hamlet D’Arcy and a wiki comment, it seems to be kind of a dud. So I’m going to declare it finished as soon as the pipeline stuff gets done, and I’ll write up some documentation and then move on to other stuff. Quite frankly, I’m having more fun playing with JoCaml than reinventing it in Java (which, after all, is kinda stupid). [...]
[...] while this is an advance, it’s not a novel invention — there is already prior art (see another reinvention here). What the Rubyists call a “DSL”, Ocamlists call “readable code”[2]. Ocaml [...]