RSS 2.0

Personal Info:

Joe Send mail to the author(s) works on parallel libraries, infrastructure, and programming models in Microsoft's Developer Division.

Blogroll:
Other
News
 C|Net
 Kuro5hin
 The Register
Technology
 <?xmlhack?>
 Daily WTF
 DevX
 Hacknot
 Java Today
 Microsoft Top 10 Downloads
 MSDN
 MSDN: "Longhorn"
 MSDN: XML Developer Center
 Slashdot
 Techdirt
 theserverside.com
 W3C
 Web Pages That Suck
 XML Cover Pages
 XML Journal
 xml.com
Technology Blogs
 Aaron Skonnard [PluralSight]
 Adam Bosworth [Google]
 Andy Rich [MS/C++]
 Arpan Desai [MS/XML]
 BCL Team [MS]
 Bill Clementson [Lisp]
 Bill de hÓra
 Bruce Eckel [J]
 Bruce Tate [J]
 Casey Chestnut
 Cedric Beust [Google]
 Chris Anderson [MS/Avalon]
 Chris Lyon [MS]
 Christian Weyer
 Clemens Vasters [newtelligence]
 Craig Andera [PluralSight]
 Dan Sugalski [Parrot]
 Daniel Cazzulino
 Dave Chappel
 Dave Roberts [Lisp]
 Dave Thomas [PragProg]
 Dave Winer
 Dion Almaer [J]
 Don Demsak
 Doug Purdy [MS/Indigo]
 Drew Marsh
 Eric Gunnerson [MS]
 Eric Rudder [MS]
 Eric Sink
 Fritz Onion [PluaralSight]
 Gavin King [J/Hibernate]
 Grady Booch [IBM]
 Hervey Wilson [MS/Indigo]
 Hillel Cooperman [MS/Shell]
 Howard Lewis Ship [J/Apache]
 Ingo Rammer [PluralSight]
 James Gosling [J/Sun]
 James Strachan [J/Groovy]
 Jason Matusow [MS/OSS]
 Jeffrey Schlimmer [MS/Indigo]
 Joe Beda [Google]
 Joel Spoelsky
 Jon Udell
 Josh Ledgard [MS/Evang]
 Joshua Allen [MS]
 Lambda
 Larry Osterman [MS]
 Maoni Stephens [MS/CLR]
 Mark Fussell [MS/XML]
 Martin Fowler
 Martin Gudgin [MS/Indigo]
 Me
 Michael Howard [MS]
 Miguel de Icaza [Mono]
 Mike Clark
 Omri Gazitt [MS/Indigo]
 Pat Helland [MS/PAG]
 Pinku Surana
 Raymond Chen [MS]
 Rich Lander [MS/CLR]
 Rob Howard
 Rob Relyea [MS/Avalon]
 Robert Cringely
 S. Somasegar [MS/DevDiv]
 Sam Gentile
 Scoble [MS/Evang]
 Scott Guthrie [MS/WebNet]
 Scott Hanselman
 Sean McGrath [J]
 Simon Fell
 Stanley Lippman [MS/C++]
 Steve Maine
 Steve Swartz [MS/Indigo]
 Steve Vinoski
 Steven Clarke [MS/Usability]
 Stuart Halloway
 Ted Leung
 Ted Neward [DM]
 Tim Bray [Sun]
 Tim Ewald [Mindreef]
 Tim O'Reilly
 Werner Vogels [Amazon]
 Wintellect
 Yasser Shohoud [MS/Indigo]
Top 20
 Brad Abrams [MS/CLR]
 Chris Brumme [MS/CLR]
 Chris Sells [MS/Ultra]
 Cyrus Najmabadi [MS/C#]
 Dominic Cooney [MS/XAF]
 Don Box [MS/Ultra]
 Don Syme [MS/R]
 Guido van Rossum [Python]
 Herb Sutter [MS/C++]
 Ian Griffiths
 Jason Zander [MS/CLR]
 Jim Hugunin [MS/CLR]
 Joel Pobar [MS/CLR]
 Krzysztof Cwalina [MS/CLR]
 Patrick Logan
 Paul Graham
 Rico Mariani [MS/CLR]
 Rory Blyth [MS/DN]
 Sam Ruby
 Wesner Moise
VC/Business Blogs
 Ed Sim
 Fred Wilson
 Jonathan Schwartz [J/Sun]
 Lawrence Lessig [Stanford]
 Mark Cuban
 Michael Hyatt
 Pierre Omidyar
 Ross Mayfield
 VentureBlog
 Weekly Read
Wine, Food & Tea
 The Silk Road of Wine
 Vinography: a wine blog
 Wine Whys

Disclaimer:
The content of this site are my own personal opinions and do not represent my employer's view in anyway.

© 2008, Joe Duffy

 
 Tuesday, February 07, 2006

I was on a mail thread today, the topic for which was the meaning—and perhaps lack of comprehensiveness—of the statement: “This type is thread safe.” Similar statements are scattered throughout our product documentation, without any good central explanation of its meaning and any caveats.

It’s relatively difficult to make such a statement. The .NET Framework is generally written so that all static members are thread-safe, while instance members are not. There are some notable exceptions, mostly to do with immutable types (e.g. the primitives, System.DateTime, System.Type, etc.), but they are infrequent.

This brings me to the types of thread safety issues we’re generally concerned with.

The first problem is torn reads and writes. Operations that deal with data whose size is greater than the native machine-sized pointer (i.e. sizeof(void*)) are not atomic in the ISA. This applies to 64-bit operations on 32-bit platforms, 128-bit on 64-bit, etc. We happen to provide you two intrinsic 64-bit types—Int64 (C# long) and Double (C# double)—which makes this issue a tad tricky. So this code

static long x;
// …
x = 1111222233334444;

consists of two DWORD MOVs at the machine level, one to the most significant half and then the least significant half (in that order, at least in the Whidbey x86 JIT). Reads likewise involve two MOV instructions.

If you’re doing naked reads and writes with such values, instructions can be interleaved such that only one DWORD has been written. That means a thread racing with the above assignment can see x with a value of (x & 0xFFFFFFFF00000000), or 2524709548 in decimal. This is obviously surprising, as the two values (at first glance) don’t seem to be related. And this same principle applies to reads and writes of any value type instances whose size is greater than sizeof(void*).

This can be solved by protecting all access to the data under a lock or via an interlocked operation. Interlocked.Exchange will do the trick for writes, and Interlocked.Read for reads. Note that most platforms offer 128-bit interlocked instructions. Unfortunately, because of the platform-specificity, the Win32 APIs and our System.Threading APIs don’t broadly support them. Hopefully this changes over time. For the same reason you often need two void*-sized writes on 32-bit, you often need the same on 64-bit.

In summary, any type that exposes a writable 64-bit field, or which returns a 64-bit value which has been copied by a field that might be in motion, is not thread-safe. And any internal reads and writes need to be done under the protection of a lock or interlocked operation. A method that updates an internal field, for example, can race with a property that returns the current value.

The second problem is read/modify/write sets of instructions. If reads and writes can be multiple instructions long, it should be clear that by default a read/modify/write is at least three. For a 64-bit value on a 32-bit platform, it’s six. At any point in that invocation, interleaved execution can cause an update to go missing. The solution here, again, is to do this inside a lock. The Interlocked.CompareExchange function is great for this purpose, as it takes advantage of hardware-level read/modify/write instructions. Thankfully they are supported by all modern hardware ISAs.

The last problem is that of ensuring coarser-grained data structure invariants can never be seen in a broken state by concurrent execution. This is especially difficult since arbitrary managed programs don’t capture such invariants in the program itself. Aside from static state, most Framework types don’t even come close to attempting to provide this level of guarantee. Static caches and lazily initialized state, for example, are places where the Framework needs to account for concurrent access. Old-style collections with SyncRoots tried to provide similar protection, but the new generic collections don’t any longer, mostly because of the performance hit you take on sequential code-paths. But those cases are the exception, not the norm.

The immutable types mentioned above are nice in that, aside from initialization-time, they never break their internal invariants. Thus, aside from assignments of instances to shared variables, you needn’t worry about any special synchronization.

In summary, any type that breaks invariants must do so in such a way that these invariants can never be observed due to concurrent execution. This means all access to data needs to be serialized with respect to coarser grained operations updating state. Our Framework isn’t written in this way, so if you share it, you usually take responsibility for locking it.

My preference is for developers to assume that all types are unsafe, and to explicitly lock when accessing them concurrently. Regardless of the documentation’s claims. We simply do not check for these things across releases, and some code that works today might break tomorrow because we accidentally forgot to account for a torn read.

 

Recent Entries:

Search:

Browse by Date:
<February 2006>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
2627281234
567891011

Browse by Category:

Notables:

Currently Up To:

Reading...

Listening...

Watching...