12 ms·
Why does this Java program terminate despite appearing that it shouldn't?
- c0deporn 13y agoYou would think that equipment valued at $12m would have safety features to prevent damage from rogue commands. Interesting.
- hga 13y agoGiven that he has the source code to the controlling software it's probably a home brew system. He's no doubt learned some important lessons, but I can't find destroying $12 million in equipment in any way funny. [c0deporn has updated his original to the more accurate and appropriate "interesting".]
- c0deporn 13y agoGood point about a home brew setup. I suppose my "funny" statement was insensitive. I only meant it was an interesting situation.
- CaveTech 13y agoIt is however very unlikely that he actually lost $12m in equipment. Most likely there was some damage done, but no where near that amount. If someone broke a $12m machine, surely their career would be over.
- ajross 13y agoThis is an important point. We're so accustomed to the low-margin consumer world sometimes that we forget that equipment can be repaired. Broken physical enclosures can be replaced, dead motors can be replaced, PCBs can be replaced and in extreme cases even the SMT components on them can be reworked for much less than the total cost of production (which likely included things like service agreements already).
- tlarkworthy 13y agohopefully the code they posted was not theirs.
- dagw 13y agoIf someone broke a $12m machine, surely their career would be over. Who would you rather have working on your $12m machine, someone with no real experience with that sort of hardware or someone with plenty of experience and some painfully learned lessons about exactly how careful you have to be when working with brittle $12m machines.
- saalweachter 13y agoEh, sometimes you fire the guy. There are two philosophies of justice: retributive and utilitarian. Retributive justice is about punishing the guilty. If you destroy a $12 million machine, you deserve to have $12 million taken away from you. Most people don't have $12 million, so the retributive boss will take away as much as he can: fire you and file suit if he can and badmouth you to other companies so you never work again. Utilitarian justice is about preventing future harm. It's not about punishing the guy who pushed the button on destroying $12 million of hardware, it's about preventing future hardware losses. You fire the guy if you think he's likely to destroy more hardware in the future, because maybe he was negligent and careless or even malicious. You keep the guy if he was just in the wrong place at the wrong time, and any other competent employee would have made the same call and inflicted the same loss. So while the "$12 million in training" line is a great ice-breaker for making the poor guy feel better, if someone broke a $1 million machine, then a $2 million machine (after all, he has $1 million of training now), then a $3 million machine, then a $6 million machine, would you say, "Great, he's got $12 million of training! Let's put him in charge of our $12 million machine!"?
- ivix 13y agoWhy do you say so? Do you suppose that a test pilot gets fired if their plane crashes?
- joering2 13y agoIf they pilot recklessly (reckless java code?), then yes they would either not make it out of flight simulator or barely make any flights until they grounded for a long time.
- neurotech1 13y agoLockheed test pilot Tom Morganfeld didn't. After crashing a YF-22 prototype, he was selected for the first X-35 test flight. Another Lockheed test pilot wrecked a F-16E at an airshow practice and ejected with injuries now flies F-35s.
- fencepost 13y agoAs punishment?
- bigchewy 13y agoactually, they just received $12M in training on how to not make that type of mistake ever again. Sounds like a great hire to me, especially if they will be undervalued in the market by others
- tootie 13y agoIt's funny because it's not me.
- deleted 13y ago[deleted]
- mpyne 13y agoThose who do not read the history of Therac-25 apparently are doomed to repeat it. If the hardware can be destructive under the control of software then you must have hardware interlocks, not just software safety features.
- kwantam 13y agoHuh. I learned a different lesson from Therac-25: "never write mission-critical software." :) For those who haven't read it, http://sunnyday.mit.edu/papers/therac.pdf http://sunnyday.mit.edu/papers/therac.pdf
- mikeash 13y agoThere are three kinds of programmers: 1. Those who read about Therac-25 and decide never to work on any systems where bugs could threaten lives (your chosen course, and mine). 2. Those who read about Therac-25, get frightened, and work very carefully on such systems. 3. Those who read about Therac-25 and think, "Those idiots! Good thing I'd never make a mistake like that." The prevalence of (3) in all professions and walks of life is kind of frightening.
- hga 13y agoYour 3 is a bit more complicated, I think: If you dig into the details of why its programmers and their managers killed people, it's very easy to say "I'd never be that irresponsible", but still screw up with much more subtle bugs.
- mikeash 13y agoThey may have been (2) and just weren't sufficiently careful. The inclusion of (3) isn't meant as a criticism of the Therac team, but rather humanity in general.
- logicallee 13y ago"My comments were not aimed at the Therac team, but at humanity in general." :)
- boothead 13y agoHow can anyone program sanely in the presence of this: currentPos = new Point(currentPos.x+1, currentPos.y+1); does a few things, including writing default values to x and y (0) and then writing their initial values in the constructor. Since your object is not safely published those 4 write operations can be freely reordered by the compiler / JVM. So from the perspective of the reading thread, it is a legal execution to read x with its new value but y with its default value of 0 for example. By the time you reach the println statement (which by the way is synchronized and therefore does influence the read operations), the variables have their initial values and the program prints the expected values. I'm not anywhere near smart or careful enough for that... I think I'll stick with Haskell.
- jlarocco 13y agoHonestly I don't think Haskell would have helped much here. That code is terrible from a thread safety perspective. If the author didn't read up on how to write good thread-safe code, it's unlikely he'd read up on Haskell, so he'd probably write bad code there, too.
- lelf 13y agoHaskell has type-safe software transactional memory for this stuff. And it's exponentially harder to get a program that compiles and has error like that.
- jlarocco 13y agoThe author didn't take the time to learn the very basics of thread safety. It's highly unlikely he would take the time to learn Haskell and software transactional memory.
- conroe64 13y agoCan you explain what makes it so terrible? At first glance it seems as if it is thread safe, and avoiding synchronization locks can provide a nice speed up. The notion that currentPos could be set to the new Point before the Point(int x, int y) constructor completed was completely surprising to me. Is that something that was obvious to you, or do you have other reasons to dislike it?
- neya 13y agoI'm curious, could he (or you) have written a program in any other languages (like Go) and not have worried about these issues? Honestly, I got some mild goose-bumps after realizing the number of Zeroes that 12 million had.
- lmm 13y agoI'd use scala actors and not have to worry about this issue. Anything that avoids shared-memory communication means you're safe from this kind of interaction. IMO raw threads are too dangerous to use as anything other than a primitive to implement higher-level concurrency on.
- Anderkent 13y agoThere are many languages where you don't have to worry about this particular issue. Clojure for example enforces immutability (which means you cannot unsafely share the reference, since the reference is immutable too) and provides safe sharing mechanisms that would prevent this.
- noelwelsh 13y agoThey could have written it in Java and not worried about these issues. I was writing Java code using channels to communicate between threads in 2000 (i.e. the stuff that Go has.) Back then we had to download Doug Lea's concurrency package to get this stuff. For about a decade it has been in the standard library.
- jtheory 13y agoEven without Doug Lea's extensions (though they did help making it more efficient and/or easier to understand), it's been possible to do this safely in Java from v1.0 -- just synchronize and notify() or notifyAll() as needed. I think the chief problem is that the developer hadn't learned the basics of multithreaded programming yet, and didn't realize s/he needed to know them. That's an extremely harsh way to learn a little lesson, regardless.
- zemo 13y agoyou could, but it's not very clear why you would want this to be multithreaded to begin with, since it appears the author wants the program to quit as soon as the value fails validation. It looks like it's multithreaded just for the hell of it. Anyway, you could do it in Go like this: http://play.golang.org/p/3Wy_S42jVj http://play.golang.org/p/3Wy_S42jVj basically what you do is you create a goroutine that reads a pointer to the Point value, checks it, and if it's bad, exits. In your main loop, send a * Point down the channel. That basically means: I made this thing. It's yours now. Do what you want with it. Then in the reporting channel, the value is read and checked. The same pointer is sent back in a reply, meaning "I'm done with this, you can have it back now". You almost certainly shouldn't be doing this; I'm just doing this to emulate the fact that the original example is using the same reference from both threads. Yes, this is very, very weird and no, you should not do this in your own programs. Anyway, we take that pointer back, dereference it, and assign it to a new Point value, which is completely weird and I still don't understand. (presumably the original author mistakenly thought this was an atomic way to set both fields on the Point.) In this way, the two goroutines (the main and reporting goroutine) are able to use channels to synchronize access to some shared memory. This is a pretty silly use of concurrency, though, because in this case, it's probably more clear to just use a mutex to lock and unlock your Point. Again, yes, this is sloppy and weird, but it represents the same behavior as the original program, but properly synchronized.
- xedarius 13y agoThat program is shocking. I'd be interested if there was even a need for threading, perhaps you could have just time stepped your actors with a simple Tick interface. for all actors actor.tick(timeDelta);
- b0b0b0b 13y agoI think the real wtf was testing in production. At least their lab now has an environment to do their qa on.
- rayiner 13y agoOne of my biggest WTF moments as a programmer was dealing with a high performance spectrum analyzer. It was a cost no object bit of experimental military hardware, and was very impressive from that perspective. But the software was buggy. We would see lockups, as well as discontinuities in the readouts, which were screwing up our post-hoc data analysis. When I dug into the code, I realized that the program used several different threads, but there was not a lock or other synchronization mechanism in sight. You had a thread pulling data off a socket from the device and storing it in a (single) memory buffer, a thread writing commands through a socket to the device, a thread pulling data out of the buffer to draw it to a waterfall plot, and a thread writing data to disk. The lockups happened because the device didn't like to change the scan state while reading data, and there was no synchronization between the command thread and the readout thread. The discontinuities happened because there was a single buffer shared by the readout thread and the write out threads, with no synchronization. So as one thread was writing out a sample, the readout thread was replacing part of that data with the next sample. I remember grepping the source for pthread_mutex or pthread_cond and slowly realizing that whoever wrote the code didn't even see the need for locks. To be fair, I later heard it was some poor hardware guy who knew some C who got roped into writing the software as an afterthought to show off the awesome hardware, a common problem with hardware companies.
- _pmf_ 13y ago> I remember grepping the source for pthread_mutex or pthread_cond and slowly realizing that whoever wrote the code didn't even see the need for locks. To be fair, I later heard it was some poor hardware guy who knew some C who got roped into writing the software as an afterthought to show off the awesome hardware, a common problem with hardware companies. "Oh, you wrote a quick demo for the sales people? Well, now it's a product sold for some ten thousand!"
- rdtsc 13y agoYou joke, but in military contract world that is totally fine. Everyone already got paid and bugs will certainly not stop the acquisition process in the future. It is all about who knows who and more about jumping around red tape certifications, ATOs, ATCs, rubber stamps and so on. That is often why soldiers on the ground end up with overpriced yet crappy tools and weapons.
- sehugg 13y agoIt's amazing what kind of yarns people on SO will spin to get help with their homework.
- angersock 13y agoCan't spin yarn without Threads! :D
- adambard 13y agoActually, vice-versa. Spinning is the act of creating threads. You might have said, "You can't make a thread without spinning yarn".
- methehack 13y agoOh @#it I fell for it.
- thurn 13y agoI often feel that it's necessary to stretch the truth a little bit on SO. Otherwise you ask "How can I do X?" and all your answers will be "Don't do X. Use Y instead."
- m_myers 13y agoThat happens because of the "old shoe or glass bottle?" question.[1] When newbies (or even oldbies) ask how to do something, often the real question could be solved much better by taking a completely different tack. This has also been called the "XY problem".[2] The best answers would ideally answer the question as asked (or explain why it can't be done) and also show a still more excellent way. [1]: http://weblogs.asp.net/alex_papadimoulis/archive/2005/05/25/408925.aspx http://weblogs.asp.net/alex_papadimoulis/archive/2005/05/25/... [2]: http://meta.stackoverflow.com/questions/66377/what-is-the-xy-problem http://meta.stackoverflow.com/questions/66377/what-is-the-xy...
- jamesbritt 13y agoThe best answers would ideally answer the question as asked (or explain why it can't be done) and also show a still more excellent way. There are times I've asked how to do X, and for various reasons X is really, no kidding, exactly what wanted to do. Still, numerous people would question my desire to do X, turning the discussion into an inquisition on motives or skill comprehension. I'm sure in some odd way they all meant well, but they were spending a lot of cycles not actually answering the question. In those cases the better strategy is to, under some crafty pretext, bluntly assert that X cannot be done. This will evoke numerous rebuttals with full details on just how incredibly wrong and foolish that claim is. Both cases play to a trait all too common to many people on discussion boards: replying to questions with the primary goal of showing that you are, in fact, smarter than everyone else.
- chris-allnutt 13y agoTHIS! is why you should always write tests.
- wtetzner 13y agoActually, concurrency is one of the areas where tests aren't particularly reliable. Whether the tests pass might depend on how the threads happened to be scheduled on that particular run.
- mark-r 13y agoTests don't always catch everything, especially in multi-threaded code. Something more was needed in this case.
- chris-allnutt 13y agoTo those who disagree with me. Testing was needed, and is reliable, not saying there is something in place that would have solved this particular instance but saying "not particularly reliable" means that you haven't properly addressed the problem. If you can't write a test that tells you your code works, then how can you as a human verify it passes all required cases manually reliably. YES, I have written tests in multi-threaded situations before. Your aversion to trying it doesn't make my statement wrong.
- anaphor 13y agoIf you look through the questioner's history it seems like they enjoy making these sorts of threading/synchronization errors, as well as getting angry/trolling. I wouldn't be surprised if the story about losing the 12 mil electron microscope is total BS designed to garner rep (I see it's getting tons of votes).
- cruise02 13y agoMost of those upvotes are from one day though, so they only get about 200 reputation due to the cap. (Also, they gave away 50 points on a bounty for the top answer, so I'm not sure if reputation was the motive or not.)
- anaphor 13y agoThat is a good point, although I still find it suspicious, especially given some of their previous questions. E.g. the one where he/she created two Haskell data types that looked the same but were using different characters in their names and then complained it wasn't working, really?.
- wissler 13y agoSide question: with $12M of equipment, wouldn't it make sense to have an independent limiter of some kind to prevent damage? Is there something about this equipment that makes that greater than $12M in difficulty? Because relying on having no bugs just so your hardware isn't destroyed seems like a bad idea...
- saym 13y agoI'm willing to bet the question has a fabricated back-story. If you look at the user's question history: there are some interesting ones, but they all could be justified as a collegiate homework problem.
- quasque 13y agoAlso, $12 million for an electron microscope seems somewhat on the expensive side, by an order of magnitude.
- Daniel_Newby 13y agoThe poor bastards might have had the entire reticle set (photo mask) set for fabricating a microchip loaded in the microscope for inspection.
- gnaffle 13y agoIt would absolutely make sense. However, my guess if the story is true would be that one (simpler, cheaper) device caused damage to other, more expensive equipment, in a way that was not foreseen. (Another similar scenario: A software controlled pump not stopping like it should, causing an entire lab bench to be flooded with corrosive liquid). Unfortunately non-software guys often have a lack of appreciation for how subtle some software bugs can be. Re. the mentioned Therac-25 incident in this thread.
- rprospero 13y agoWell, $12m breaks my personal best, but the independent limiter is only delaying the inevitable. You'll have just as many bugs in your hardware as in your software: 1) Include software controls to limit motor position 2) Include hardware kill switch to prevent collisions. 3) Drive unit to the outermost SAFE motor position. 4) Motor limit drops into worm gear 5) Send unit back to original position. 6) Unit doesn't move as worm gear eats limit cable. 7) Since the unit didn't actually move, the software limits are now compared with faulty data. 8) Send the unit out to the far edge again 9) Since the software positions are incorrect, the software limits won't help. 10) Since the limit cable was eaten by the gear, the contact switch won't help. 11) Hear super-mirror array drive into concrete wall. Of course, having a hardware limit switch the trigger on an open circuit would have fixed that problem. Until the time that a faulty solder joint shorts the switch and the mirror drives into the wall. So you can add in a hardware encoder to check the physical position. Which saves you until the unit is manually moved next to the wall between runs without updating the position in software. Since the encoder only gives relative position, the software limits, encoder, and shorted limit switch still gleefully watch as you again drive the super-mirror into a wall.
- methehack 13y agoWithout looking at it too closely, there's no synchronization on the point. The synchronized this (with an empty block - wtf -- that's meaningless) has nothing to do with the point...so the point is possibly stale. I haven't assessed the logic more than that to see if that's a possible state that would program exit, but it's the first thing I noticed. Also, this program, as others have pointed out, is weirdly complex -- and that's a euphemism.
- a-priori 13y agoI don't believe the synchronized(this){} block is entirely meaningless. Essentially it acts as a gate where execution will halt until all other synchronized(this) blocks are complete before continuing. That's not to say another concurrent access can't sneak in between the block and the next statement, of course, but it does change the behaviour somewhat. But I can't really think of a use case. As a side effect, it may also act as a memory barrier.
- conroe64 13y agoI think the logic is more to elicit a strange occurrence (bug, dare I say?) that doesn't occur unless under some weird circumstances. In this case, this behavior would be possible unless the value of a variable, currentPos, is set to an object before that object's constructor finished, and all in the same thread! The only possible explanation is that the JIT decided to reorder the execution so the variable was set to memory allocated for the object, and then ran the objects constructor. It's all very surprising behavior.
- viraptor 13y agoOh come on! This title rewriting is getting silly now. The Java application is not the story - there are thousands of those on SO. The story is the (real or made up) destruction of equipment by a failing program...
- robomartin 13y agoAt the core, the use of threads for any embedded system required to operate in hard real time is a mistake. Threads and interrupts are the minefields of real-time embedded systems. Based on study and years of experience I conclude that the ONLY way to build such as system is to use a time-triggered approach as opposed to event-triggered. No threads. Only one interrupt --the timer-- in the entire system if at all possible. This is where I fall back to the raw simplicity of C. You really have to work hard to do stupid shit like threads in C. By that I mean that you have to go out of your way to bring in the code and a framework that might allow you to do that. I've successfully written and applied at least a couple of RTOS's for use in mission critical applications (definition: you fuck up and someone gets hurt or dies). Again, only one interrupt. No threads. Carefully --and I do mean very carefully-- shared data between tasks only if absolutely necessary. A corollary to this is that I could not fathom programming an electron microscope (the subject of the SO question) or anything that might lead to millions of dollars in losses with Java. Couldn't pay me enough. Anyone interested in working with hard real-time embedded systems would be wise to study the following well-known books: - Patterns for Time-Triggered Embedded Systems, Michael J. Pont - Embedded Systems Building Blocks, Jean J. Labrosse - uC/OS-III, Jean J. Labrosse (uC/OS-II by the same author includes the source to the prior version of the OS) - Doing Hard Time, Grady Booch - Operating System Concepts, Silbershatz, Galvin, Gagne There are other good books on the subject. The above should provide a very solid foundation. Don't just hack away without the necessary background. The consequences of making mistakes out of sheer ignorance could be dire.