dummies
 

Suchen und Finden

Titel

Autor/Verlag

Inhaltsverzeichnis

Nur ebooks mit Firmenlizenz anzeigen:

 

The Complete Friday Q&A: Volume I

Mike Ash

 

Verlag Mike Ash, 2011

ISBN 9781609847067 , 917 Seiten

Format ePUB

Kopierschutz frei

Geräte

34,59 EUR


 

Related Articles

Greetings one and all. I caught my mistaken writing of "2008" in this blog post title almost instantly instead of only noticing after I'd already posted it like I did last week, so the year must be coming along. Welcome to the second Friday Q&A of 2009 (and only the fourth in all human history!) where I'll be taking Ed Wynne's suggestion and talking about the various meanings and implications of thread safety as they apply to Mac OS X system frameworks.

Thread Safety? What's That?
Hopefully not too many readers are actually asking the above questions, but just as a quick refresher, thread safety is about whether it's safe to access a particular module, API, or data structure from multiple threads. These things are typically unsafe due to making assumptions of single-threadedness, such as updating multiple pieces of data in a non-atomic fashion, in such a way as to expose inconsistent data to the outside world. There's the classic example:

x++;

Which is not thread safe (assuming x is globally accessible) because down at the very bottom it breaks down into multiple operations:

get x increment store x

And if multiple threads are doing this at once, they interleave and you miss increments. Not too dire here, but apply it to pointers and objects and you can hopefully see why you'll crash at best, and silently corrupt data if you're unlucky.

So to start off with there are two kinds of thread safety in the world:

  1. Not thread safe. The normal state. Code is not thread safe by default. Special effort needs to be taken to make it thread safe, and if you haven't done it, your code falls into this category.
  2. Thread safe. Can be called from any thread without a care or worry. Nice to have, often painful to make.

Three Kinds
But what does this really mean? Well, thread safe is easy enough to understand. But not thread safe can't really mean it can't be called from any thread, because all code runs from some thread.

Of course what it really means is that this code can't be run from more than one thread at the same time.

But that doesn't really do it either. For example, NSMutableArray is not thread safe. But you can call NSMutableArray from multiple threads simultaneously, as long as each thread is working on a different array. So maybe we should say that thread unsafe means that the code can't be run on the same data from more than one thread at the same time.

Well, that's better, but still not there. Take the atoi() function. Not thread safe, says so in the man page. But you only ever feed it constant data, and it's unsafe even if you feed it completely different data on your different threads. What's the deal? Simple: behind the scenes, it has some shared data.

How can you tell one from the other? We'll need another classification:

  1. Never thread safe. The normal state. Code is not thread safe by default. Special effort needs to be taken to make it thread safe, and if you haven't done it, your code falls into this category.
  2. Not thread safe with shared data. Can safely be called from multiple threads simultaneously as long as each thread is dealing with a distinct set of data.
  3. Thread safe. Can be called from any thread without a care or worry. Nice to have, often painful to make.

It's actually really easy to write code that falls into category #2. All you have to do is not have any global state, which is pretty common anyway. If you're writing an array class, your method for adding a new object to the array isn't going to deal with global state, it's going to deal with that one array. So while #1 may be the "normal state", #2 is actually really easy to come by, and most code falls into that category.

The System Screws It All Up
These categories are sufficient in a relatively simplistic program which controls every action taking place and for which all the code is known. It gets more complicated when you start pulling in a ton of big, complex external frameworks such as AppKit and Foundation. Take NSView as an example. It can fall into category #1 or #3 depending on what you're doing with it. (Drawing is safe, creation/resizing/etc. is unsafe.) But that #1 is complicated by the fact that the shared global data which makes NSView unsafe can be accessed by code that isn't yours.

NSView isn't just unsafe from multiple threads, it's main thread only. This is because your NSView doesn't just belong to you, it belongs to the framework. And this means that you can't synchronize all accesses to it, because some of those accesses come from code that does not belong to you! Let's put this in its own paragraph, because it's important:

If an API is never thread safe and you do not absolutely control every access to this API, then you can only call it from the main thread.

And since virtually every system API is going to be, at least potentially, called by other system APIs, we can rewrite our three types of thread safety:

  1. Main thread only. The normal state. Code is not thread safe by default. Special effort needs to be taken to make it thread safe, and if you haven't done it, your code falls into this category.
  2. Not thread safe. Can safely be called from multiple threads simultaneously as long as each thread is dealing with a distinct set of data.
  3. Thread safe. Can be called from any thread without a care or worry. Nice to have, often painful to make.

Singletons
Keep in mind that singletons qualify as global shared data. This has an important impact on their thread safety. Practically speaking, it means that singletons provided by system frameworks only ever fall into category #1 or #3. Take NSFileManager as an example. It's listed as not being thread safe. What this really means is that [NSFileManager defaultManager] can only be safely used from the main thread, because you can't control what other code might access it. (On 10.5 and above you can alloc/init your own private instances which then fall into category #2.)

Terminology and the Apple Way
This is all fine and dandy except that Apple, in their infinite wisdom, does not always distinguish between main thread only and not thread safe. To make things worse, they even sometimes use the term thread safe to mean what we have defined here as not thread safe.

Let's take that second one first, because it's pretty weird. As a concrete example, look at the CFNetDiagnostics API. The documentation for this API is full of quotes like this:

This function is thread safe as long as another thread does not alter the same CFNetDiagnosticRef at the same time.

Huh??

So why is it labeled "thread safe"? What they're trying to convey here, through the fog of inadequate terminology, is that this API falls into category #2 and not category #1. In other words, you can use it from any thread as long as only one thread at a time is using this API on any given piece of data. This as opposed to an API which requires you to call it only from the main thread.

Other APIs are less explicit about it. The Search Kit reference simply states "Search Kit is thread-safe". And yet I'm pretty sure it's not. Again, it's trying to convey that Search Kit is in category #2 rather than category #1.

Why do they do this? Well, back in the day, on the classic Mac OS, nearly all code ran in what might be considered the "main thread" today. As a consequence, nearly every API required only calling it from there. Being able to run from multiple threads was novel and unusual and was worth documenting. Alas, not only does this no longer make sense on Mac OS X, but this sort of terminology abuse is actively destructive because it ends up making guarantees which...