| // Copyright 2008 Google Inc. All Rights Reserved. |
| |
| #include <common/logging.h> |
| #include "gutil/logging-inl.h" |
| #include "gutil/once.h" |
| #include "gutil/dynamic_annotations.h" |
| #include "gutil/spinlock_internal.h" |
| |
| // All modifications to a GoogleOnceType occur inside GoogleOnceInternalInit. |
| // The fast path reads the variable with an acquire-load.. |
| // This is safe provided we always perform a memory barrier |
| // immediately before setting the value to GOOGLE_ONCE_INTERNAL_DONE. |
| |
| void GoogleOnceInternalInit(Atomic32 *control, void (*func)(), |
| void (*func_with_arg)(void*), void* arg) { |
| if (DEBUG_MODE) { |
| int32 old_control = base::subtle::Acquire_Load(control); |
| if (old_control != GOOGLE_ONCE_INTERNAL_INIT && |
| old_control != GOOGLE_ONCE_INTERNAL_RUNNING && |
| old_control != GOOGLE_ONCE_INTERNAL_WAITER && |
| old_control != GOOGLE_ONCE_INTERNAL_DONE) { |
| LOG(FATAL) << "Either GoogleOnceType is used in non-static storage " |
| "(where GoogleOnceDynamic might be appropriate), " |
| "or there's a memory corruption."; |
| } |
| } |
| static const base::internal::SpinLockWaitTransition trans[] = { |
| { GOOGLE_ONCE_INTERNAL_INIT, GOOGLE_ONCE_INTERNAL_RUNNING, true }, |
| { GOOGLE_ONCE_INTERNAL_RUNNING, GOOGLE_ONCE_INTERNAL_WAITER, false }, |
| { GOOGLE_ONCE_INTERNAL_DONE, GOOGLE_ONCE_INTERNAL_DONE, true } |
| }; |
| // Short circuit the simplest case to avoid procedure call overhead. |
| if (base::subtle::Acquire_CompareAndSwap(control, GOOGLE_ONCE_INTERNAL_INIT, |
| GOOGLE_ONCE_INTERNAL_RUNNING) == GOOGLE_ONCE_INTERNAL_INIT || |
| base::internal::SpinLockWait(control, ARRAYSIZE(trans), trans) == |
| GOOGLE_ONCE_INTERNAL_INIT) { |
| if (func != nullptr) { |
| (*func)(); |
| } else { |
| (*func_with_arg)(arg); |
| } |
| ANNOTATE_HAPPENS_BEFORE(control); |
| int32 old_control = base::subtle::NoBarrier_Load(control); |
| base::subtle::Release_Store(control, GOOGLE_ONCE_INTERNAL_DONE); |
| if (old_control == GOOGLE_ONCE_INTERNAL_WAITER) { |
| base::internal::SpinLockWake(control, true); |
| } |
| } // else *control is already GOOGLE_ONCE_INTERNAL_DONE |
| } |