Fix race conditions in mono_lazy_initialize,
authorJay Krell <jaykrell@microsoft.com>
Fri, 14 Jun 2019 07:52:48 +0000 (00:52 -0700)
committerMarek Safar <marek.safar@gmail.com>
Mon, 17 Jun 2019 13:31:35 +0000 (15:31 +0200)
commit827e81643b91ce6e331e0dae428b9e9948bbe1f1
tree8fb14b761192ffc8ec2e0fac94a7f0cca6dce911
parent11bfec8f09abd595792f0a920fed362366e0d158
Fix race conditions in mono_lazy_initialize,
which is meant to be all about handling race conditions.

1. Less severe:
Add missing read barrier to mono_lazy_initialize().

This is unfortunate.

The general pattern of:

if initialized
  use the data
else:
  initialize the data, possibly with lots of locks and barriers
  mark initialized

is racy, because "use the data" can be scheduled ahead of "if initialized".

"Barriers come in pairs" generally, and this was missing one.

It depends somewhat. If the data is all pointers, initialized in the else path,
from null to non-null, and use includes dereferencing, which is a common
but not universal case, then it is ok on all non-Alpha processors, due
to "data dependency".

But if the data includes reading globals, then there is a race.

This is a bit of a slow down on fast paths on arm, and possibly
other architectures.

There are barrier-free ways to solve this, involving a thread local,
that should seriously be considered, and applied throughout the runtime.

The runtime has a lot of on-demand initialization and a lot of looks racy.

See www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm.
Which would have to be adopted to be coop-friendly which should not be difficult.

Notice that the existing "lazy" mechanism is also not coop-friendly.
That is not changed by this PR.

2. Change `status` to `*lazy_init` to fix severe race condition.

Commit migrated from https://github.com/mono/mono/commit/7d824dc2ae19e8bfb712ee057a3ccdf1c08564bd
src/mono/mono/utils/mono-lazy-init.h