From cc0e92a3c359cb34079b8e5b258ceef813ea684a Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Mon, 30 Nov 2015 00:47:54 -0500 Subject: [PATCH] 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 --- lib/base/heimbase.c | 19 +++++++++++++++---- lib/base/heimbase.h | 5 ++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/base/heimbase.c b/lib/base/heimbase.c index 8f5694d27..5642b1cd3 100644 --- a/lib/base/heimbase.c +++ b/lib/base/heimbase.c @@ -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) diff --git a/lib/base/heimbase.h b/lib/base/heimbase.h index c617a6344..7a4ec385d 100644 --- a/lib/base/heimbase.h +++ b/lib/base/heimbase.h @@ -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 */