base: correct Windows heim_base_once_f semantics

As implemented by Jeffrey Altman heim_base_once_f() is a variant of the
"double-checked gate lock pattern".   Full memory barriers must be used
when determining whether or not to call SwitchToThread().

Change-Id: I2f8446a56c50a37c921d6e993433c9a3f7488f50
This commit is contained in:
Nicolas Williams
2015-11-30 00:47:54 -05:00
committed by Jeffrey Altman
parent 4735faba59
commit cc0e92a3c3
2 changed files with 17 additions and 7 deletions

View File

@@ -369,12 +369,23 @@ void
heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *))
{
#if defined(WIN32)
if (InterlockedCompareExchange(&once->started, 1L, 0L) == 0L) {
once->running = 1L;
/*
* State 0 means that func() has never executed.
* State 1 means that func() is executing.
* State 2 means that func() has completed execution.
*/
if (InterlockedCompareExchange(&once->state, 1L, 0L) == 0L) {
/* State is now 1 */
(*func)(ctx);
once->running = 0L;
(void)InterlockedExchange(&once->state, 2L);
/* State is now 2 */
} else {
while (once->running)
/*
* The InterlockedCompareExchange is being used to fetch
* the current state under a full memory barrier. As long
* as the current state is 1 continue to spin.
*/
while (InterlockedCompareExchange(&once->state, 2L, 0L) == 1L)
SwitchToThread();
}
#elif defined(HAVE_DISPATCH_DISPATCH_H)

View File

@@ -58,10 +58,9 @@ typedef heim_object_t heim_bool_t;
typedef heim_object_t heim_null_t;
#if defined(WIN32)
typedef struct {
LONG started;
LONG running;
LONG state;
} heim_base_once_t;
# define HEIM_BASE_ONCE_INIT {0L, 0L}
# define HEIM_BASE_ONCE_INIT {0L}
#else
# define HEIM_BASE_ONCE_INIT 0
typedef long heim_base_once_t; /* XXX arch dependant */