
A Beautiful Race Condition - shawndumas
http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html
======
pmjordan
Nice analysis and demonstration that even relaxing concurrency requirements is
not as easy as it looks.

Interestingly, when trying to reason about and verify correct synchronisation
of data structures, I immediately start drawing boxes with lots of arrows for
pointers (just like those in the article) even though I don't normally need
any visual aids to design my code. Somehow, holding a complex data structure
in my head is no problem, but as soon as I try to think about concurrent
access to something as simple as a linked list, I need to draw each state.
Anyone else got any good strategies for concurrency analysis?

~~~
ComputerGuru
I used to have do something similar (on a whiteboard, though) whenever I
needed to write some slightly complicated concurrency code (I'm a systems
developer). However, after years of writing multi-threaded and synchronization
code, I've learned that the trick is to.... abstract (?) the representation in
your head.

If you try to "flatten out" the concurrency, it'll be this HUGE representation
that'll never fit in your skull. The trick is to think in multiple dimensions
instead of flattening it out. Sort of queue the other threads in the back of
your head, and pretend you're the OS. Whenever you reach a line of code for
the current thread you're "imagining/debugging in your head", loop over the
other consumers/producers in your model and ask yourself "what happens to this
guy at this moment, or what happens if this guy was doing x, y, or z?"

Basically, don't hold the structure, hold the events. Expand the other objects
as needed in your head, then stash them away. Don't try to hold them all in
there, because they'll never fit.

Incidentally, since adapting to this I many times still get the urge to pick
up a pen or marker and scribble down the concurrency model - except 2 seconds
later I give up because it's so hard to efficiently represent multi-threaded
environments/code in 2d. And that's the same difficulty you have trying to jam
that representation in your head.

Remember the description of the components, not the components themselves, I
guess. I hope this isn't as confusing as I fear I've made it out to be!

~~~
pmjordan
Yeah, I do that for simpler things (a bunch of mutexes, condition variables,
etc.) but as soon as I look at anything lock-free, by brain just fails to take
it all in.

------
Confusion
I remember reading this article for the first time and being overcome by the
urge to erase it from my memory. Every time it comes up I worry that I may
have committed this error in applications still in production. I know for sure
that I've wondered on multiple occasions about whether you could use a regular
HashMap as an imperfect cache and I'm not sure I've always (especially when I
first started writing code) erred on the safe side of caution and opted for
the ConcurrentHashMap.

------
btrask
If you have a library class or function that is not explicitly marked as
thread safe, do not assume it is, regardless of whether or not it "should" be
under any system of logic or reason.

Why not?

Because you have no idea what it might be doing behind the scenes. Even
"immutable" objects might have mutable state under the covers of encapsulation
(e.g. caching).

And even if you somehow manage to prove that the current version IS safe,
perhaps the next version won't be. After the code has been running perfectly
for years and the library has been upgraded dozens of times, good luck every
finding your subtle, intermittent threading disaster.

Thread safety is an implementation detail unless you are explicitly told
otherwise.

------
JoeAltmaier
Nice analysis. Points up the need for synchronization primitives around
complex data structures.

Which brings up its own issues: scheduling blockages. Resizing a hash map;
garbage collecting; flushing file buffers. It doesn't matter, putting
synchronication primitives around usually-fast-but-occasionally-dog-slow
operations is the sad result of most naieve multithreaded attempts.

It results in apparently-hung GUI threads, stuttering performance, deadlocks
that usually don't happen and are terrible to diagnose.

------
gallerytungsten
I don't have anything to add regarding race conditions. But I do want to
mention how awesome the mailinator service is. I've been using it for years
and recommend it highly.

------
chicagobob
Cool analysis. tl;dr:

If you're using a non-thread safe structure in a multi-threaded way and think
you understand what kinds of data corruption you might get (and are ok with
that), you still might get totally different and unexpected kinds of data
corruption than you planned for (read the article for details).

------
spektom
Reading javadoc helps as well sometimes. The HashMap's doc says "Note that
this implementation is not synchronized", and that's enough for me for not
using this class as global unsynchronized cache.

------
sophacles
So, the question is: does a hashmap implementation exist where accessing and
normal writing is not serialized, but expanding the table has the appropriate
locks?

~~~
mlni
Here you go:

Collections.synchronizedMap(new HashMap());

~~~
riffraff
he wrote "accessing is not serialized"

------
fleitz
That's why I use Microsoft.FSharp.Collections.Map instead. No multi-threading
issues with concurrent access and no performance killing locks.

------
dkarl
A very cool analysis. I will be filing this under "what part of 'undefined'
don't you understand?"

------
TheAmazingIdiot
This is a complete aside from any contents from the article...

This is what I see....

23."You will be lynched" 24.A Beautiful Race Condition

That's Just Wrong. Completely by accident, but still Just Wrong.

