Hacker News new | ask | show | jobs
by btschaegg 3361 days ago
> I'm positive the people who maintain it are aware of the tradeoffs between fair and unfair locks and made the default fair intentionally.

I think you're neglecting a couple of less-technical factors here. Yes, fairness was certainly made the default for reasons at some point (but still, even then, one might argue that an opt-in solution might have been better). On the other hand, there's the likely possibility that those reasons don't really hold true anymore. Think of Scene Graphs vs. Entity Component Systems in high-performance videogame design - in this example the rise of caching made a whole architecture out-dated.

On the other hand, like removing the GIL in Python, such decisions are not to be taken lightly because of the things you will break. It's very likely that there are applications that would still have problems with starving threads, and just switching from opt-out to opt-in will make them break for no apparent reason in the strangest of circumstances. I know, Apple likes to break things more often than MS, but I'd guess that´s not a risk they're willing to take. Imagine you're updating your OS and a dozen apps that worked for a decade and don´t get updates anymore start behaving strangely.

So, it's not unreasonable to settle for a less-than-optimal solution that still keeps things working and only makes things slower in the worst-case scenario. That doesn't mean it's not open to criticism, though.

2 comments

> Yes, fairness was certainly made the default for reasons at some point (but still, even then, one might argue that an opt-in solution might have been better).

I think an unfair or opt-in-fair solution was deemed worse than fair-by-default for the simple reason that the most straightforward way to implement mutexes is

  enter kernel mode
  maybe grab a spinlock if on SMP
  add yourself to the waiting list
  sleep until woken up

  do critical section

  enter kernel mode
  spinlock
  wake the first waiting thread
That's a functionality every mutex must have and it happens to give fair semantics for free. Making semantics weaker for the purpose of optimization (and not just for the sake of making application developer's life harder or future flexibility which may happen to never be needed) actually takes additional work on top of that.

Since we are talking about a uniprocessor desktop OS developed in the nineties, it's plausible they didn't care about mutex performance as much as today and giving this extra guarantee afforded by their simple implementation seemed reasonable at the time.

note that every half decent mutex implementation will not enter the kernel (in neither lock or unlock) unless the mutex is contended.
Sure, but OP's benchmark clearly shows that OS X's default mutex is at most 5% decent ;)
I'd love to see them perform the same benchmark on macOS 10.12. Pthread mutexes were sped up in "the new OSes" according to a tweet made last year (sorry no link), though I'm not sure if that was referring to 10.11 or 10.12. Either way, the benchmark should perform better now, and I'm curious how much. I'd run it myself except those numbers wouldn't be comparable to OP's Mac Mini benchmark. Of course, OP's Linux benchmark numbers aren't either, because it's a different (and probably much more powerful) machine.
...which comes back to my point that old truths are more likely just not as true anymore.
Apple can and does update APIs in ways that preserve old behavior for apps linked against older SDKs, specifically so old apps continue to work. They could do the same here with versioned symbols, if they wanted to switch mutexes to unfair-by-default. I believe they intentionally go with fair-by-default because it's the safe choice, the choice that guarantees program correctness, since most people don't think about this sort of thing and probably won't be able to figure out themselves when their locks should be fair or unfair. It's the same reason C11 and C++11 atomics default to sequential consistency when not otherwise specified; that's the slowest memory ordering, but it's the one that's guaranteed to be correct for all cases. If you know what you're doing you can override the default and pick something else.
> Apple can and does update APIs in ways that preserve old behavior for apps linked against older SDKs, specifically so old apps continue to work.

Fair point, although that still won't rule out applications that get updates (and thus still would have to be completely re-evaluated on the basis of such a case).

Also, wouldn't e.g. Homebrew also get those problems if you compile against the new SDK? (Non-Mac user here, so maybe I've got the wrong impressions on that...)

> I believe they intentionally go with fair-by-default because it's the safe choice [...]

Safety might be a big concern, but on the other hand, pthreads is a standard - if the article's right, and POSIX doesn't mandate fairness, you might still argue that this "addon" was better put into a custom solution than the other way around.

Then again, I've always suspected that strange implementations (or a total lack thereof) of POSIX must be one of the main reasons why Boost exists...

> Also, wouldn't e.g. Homebrew also get those problems if you compile against the new SDK?

Yes, but nearly all Homebrew software is cross-platform unix software, so that software will already have to deal with having unfair mutexes on Linux.

> … pthreads is a standard - if the article's right, and POSIX doesn't mandate fairness …

It doesn't mandate fairness, but it doesn't mandate unfairness either. Apple platforms defaulting to fair is no less conforming than Linux defaulting to unfair.

> Apple platforms defaulting to fair is no less conforming than Linux defaulting to unfair.

Of course, but it makes it less performant in many situations, while you can't count on that feature if you're coding against the standard (i.e. if for cross platform development; hence OP's blogpost). Which - in turn - explains why people are complaining about the performance, since they need to guarantee the fairness themselves anyway if they really need it.

On the other hand: If you're only coding against OSX, having the fairness in an extension shouldn't be a problem anyway.

Of course, this then facilitates other problems, if the standard is not evolving fast enough (the whole OpenGL extension mess would be a good example).

I don't think Apple makes decisions about their OS with the mindset of "what will someone who's writing a cross-platform CLI tool that happens to work on OS X expect?". Making mutexes unfair by default would make it behave more like other platforms that cross-platform tools expect, but at the cost of making software written specifically for OS X not be correct-by-default.

In general, when picking defaults and choosing between "correct" or "performant", the decision is usually made in favor of "correct", because incorrectness should always be an opt-in thing. I'm actually kind of surprised Linux and Windows default to unfair locks.

That's the point though, if you're Writing threading code specifically for Windows, you're not writing pthreads code. So all MS has to care about is that their custom threading abstraction is safe.

POSIX is more of a common denominator than a good choice in many usecases (you'll never get far if you're dealing with complicated file handling for example[1]), but it is an essential baseline for cross-platform development. If you're changing too much about it, you're burdening cross-platform devs to do system-specific implementations anyway, so there's not much point to it.

As a flipside, you'll also get devs who see a guarantee on one system, assume the standard guarantees it, and then make statements about their library that are untrue (i.e. "WTF::Lock is fair").

[1]: https://youtu.be/uhRWMGBjlO8