diff --git a/icu4c/source/common/mutex.cpp b/icu4c/source/common/mutex.cpp index 8da3c7e0f6..e1e502d4ad 100644 --- a/icu4c/source/common/mutex.cpp +++ b/icu4c/source/common/mutex.cpp @@ -1,7 +1,7 @@ /* ******************************************************************************* * -* Copyright (C) 2008-2010, International Business Machines +* Copyright (C) 2008-2011, International Business Machines * Corporation and others. All Rights Reserved. * ******************************************************************************* @@ -13,6 +13,7 @@ #include "unicode/utypes.h" #include "mutex.h" +#include "uassert.h" U_NAMESPACE_BEGIN @@ -23,22 +24,49 @@ void *SimpleSingleton::getInstance(InstantiatorFn *instantiator, const void *con if(U_FAILURE(errorCode)) { return NULL; } - void *instance; - UMTX_CHECK(NULL, fInstance, instance); + // TODO: With atomicops.h: void *instance = (void*)Acquire_Load(&fInstance); + // and remove UMTX_ACQUIRE_BARRIER below. + void *instance=ANNOTATE_UNPROTECTED_READ(fInstance); + UMTX_ACQUIRE_BARRIER; + ANNOTATE_HAPPENS_AFTER(&fInstance); if(instance!=NULL) { return instance; - } else { - instance=instantiator(context, errorCode); - Mutex mutex; - if(fInstance==NULL && U_SUCCESS(errorCode)) { - fInstance=instance; - } else { - duplicate=instance; - } - return fInstance; } + + // Attempt to create the instance. + // If a race occurs, then the losing thread will assign its new instance + // to the "duplicate" parameter, and the caller deletes it. + instance=instantiator(context, errorCode); + UMTX_RELEASE_BARRIER; // Release-barrier before fInstance=instance; + Mutex mutex; + if(fInstance==NULL && U_SUCCESS(errorCode)) { + U_ASSERT(instance!=NULL); + ANNOTATE_HAPPENS_BEFORE(&fInstance); + // TODO: With atomicops.h: Release_Store(&fInstance, (AtomicWord)instance); + // and remove UMTX_RELEASE_BARRIER above. + fInstance=instance; + } else { + duplicate=instance; + } + return fInstance; } +/* + * Three states: + * + * Initial state: Instance creation not attempted yet. + * fInstance=NULL && U_SUCCESS(fErrorCode) + * + * Instance creation succeeded: + * fInstance!=NULL && U_SUCCESS(fErrorCode) + * + * Instance creation failed: + * fInstance=NULL && U_FAILURE(fErrorCode) + * We will not attempt again to create the instance. + * + * fInstance changes at most once. + * fErrorCode changes at most twice (intial->failed->succeeded). + */ void *TriStateSingleton::getInstance(InstantiatorFn *instantiator, const void *context, void *&duplicate, UErrorCode &errorCode) { @@ -46,37 +74,58 @@ void *TriStateSingleton::getInstance(InstantiatorFn *instantiator, const void *c if(U_FAILURE(errorCode)) { return NULL; } - int8_t haveInstance; - UMTX_CHECK(NULL, fHaveInstance, haveInstance); - if(haveInstance>0) { - return fInstance; // instance was created - } else if(haveInstance<0) { - errorCode=fErrorCode; // instance creation failed - return NULL; - } else /* haveInstance==0 */ { - void *instance=instantiator(context, errorCode); - Mutex mutex; - if(fHaveInstance==0) { - if(U_SUCCESS(errorCode)) { - fInstance=instance; - instance=NULL; - fHaveInstance=1; - } else { - fErrorCode=errorCode; - fHaveInstance=-1; - } - } else { - errorCode=fErrorCode; - } - duplicate=instance; - return fInstance; + // TODO: With atomicops.h: void *instance = (void*)Acquire_Load(&fInstance); + // and remove UMTX_ACQUIRE_BARRIER below. + void *instance=ANNOTATE_UNPROTECTED_READ(fInstance); + UMTX_ACQUIRE_BARRIER; + ANNOTATE_HAPPENS_AFTER(&fInstance); + if(instance!=NULL) { + // instance was created + return instance; } + + // The read access to fErrorCode is thread-unsafe, but harmless because + // at worst multiple threads race to each create a new instance, + // and all losing threads delete their duplicates. + UErrorCode localErrorCode=ANNOTATE_UNPROTECTED_READ(fErrorCode); + if(U_FAILURE(localErrorCode)) { + // instance creation failed + errorCode=localErrorCode; + return NULL; + } + + // First attempt to create the instance. + // If a race occurs, then the losing thread will assign its new instance + // to the "duplicate" parameter, and the caller deletes it. + instance=instantiator(context, errorCode); + UMTX_RELEASE_BARRIER; // Release-barrier before fInstance=instance; + Mutex mutex; + if(fInstance==NULL && U_SUCCESS(errorCode)) { + // instance creation newly succeeded + U_ASSERT(instance!=NULL); + ANNOTATE_HAPPENS_BEFORE(&fInstance); + // TODO: With atomicops.h: Release_Store(&fInstance, (AtomicWord)instance); + // and remove UMTX_RELEASE_BARRIER above. + fInstance=instance; + // Set fErrorCode on the off-chance that a previous instance creation failed. + fErrorCode=errorCode; + // Completed state transition: initial->succeeded, or failed->succeeded. + } else { + // Record a duplicate if we lost the race, or + // if we got an instance but its creation failed anyway. + duplicate=instance; + if(fInstance==NULL && U_SUCCESS(fErrorCode) && U_FAILURE(errorCode)) { + // instance creation newly failed + fErrorCode=errorCode; + // Completed state transition: initial->failed. + } + } + return fInstance; } void TriStateSingleton::reset() { fInstance=NULL; fErrorCode=U_ZERO_ERROR; - fHaveInstance=0; } #if UCONFIG_NO_SERVICE diff --git a/icu4c/source/common/mutex.h b/icu4c/source/common/mutex.h index ea2e3485d8..7f7ef897af 100644 --- a/icu4c/source/common/mutex.h +++ b/icu4c/source/common/mutex.h @@ -1,7 +1,7 @@ /* ****************************************************************************** * -* Copyright (C) 1997-2010, International Business Machines +* Copyright (C) 1997-2011, International Business Machines * Corporation and others. All Rights Reserved. * ****************************************************************************** @@ -113,7 +113,7 @@ struct SimpleSingleton { #define STATIC_SIMPLE_SINGLETON(name) static SimpleSingleton name={ NULL } /** - * Handy wrapper for an SimpleSingleton. + * Handy wrapper for a SimpleSingleton. * Intended for temporary use on the stack, to make the SimpleSingleton easier to deal with. * Takes care of the duplicate deletion and type casting. */ @@ -145,7 +145,6 @@ private: struct TriStateSingleton { void *fInstance; UErrorCode fErrorCode; - int8_t fHaveInstance; /** * Returns the singleton instance, or NULL if it could not be created. @@ -167,10 +166,10 @@ struct TriStateSingleton { void reset(); }; -#define STATIC_TRI_STATE_SINGLETON(name) static TriStateSingleton name={ NULL, U_ZERO_ERROR, 0 } +#define STATIC_TRI_STATE_SINGLETON(name) static TriStateSingleton name={ NULL, U_ZERO_ERROR } /** - * Handy wrapper for an TriStateSingleton. + * Handy wrapper for a TriStateSingleton. * Intended for temporary use on the stack, to make the TriStateSingleton easier to deal with. * Takes care of the duplicate deletion and type casting. */ diff --git a/icu4c/source/common/umutex.h b/icu4c/source/common/umutex.h index 9336fe8aa7..fed2196ec8 100644 --- a/icu4c/source/common/umutex.h +++ b/icu4c/source/common/umutex.h @@ -1,6 +1,6 @@ /* ********************************************************************** -* Copyright (C) 1997-2008, International Business Machines +* Copyright (C) 1997-2011, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * @@ -19,8 +19,26 @@ #define UMUTEX_H #include "unicode/utypes.h" -#include "unicode/uclean.h" +#include "unicode/uclean.h" +#if defined(U_WINDOWS) +# include +#endif + +#if defined(U_DARWIN) +# include +#endif + +/* + * If we do not compile with dynamic_annotations.h then define + * empty annotation macros. + * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations + */ +#ifndef ANNOTATE_HAPPENS_BEFORE +# define ANNOTATE_HAPPENS_BEFORE(obj) +# define ANNOTATE_HAPPENS_AFTER(obj) +# define ANNOTATE_UNPROTECTED_READ(x) (x) +#endif /* APP_NO_THREADS is an old symbol. We'll honour it if present. */ #ifdef APP_NO_THREADS @@ -33,47 +51,60 @@ * Default: use threads. * Even with thread support compiled out, applications may override the * (empty) mutex implementation with the u_setMutexFunctions() functions. - */ + */ #ifndef ICU_USE_THREADS # define ICU_USE_THREADS 1 #endif -/** - * By default assume that we are on a machine with a weak memory model, - * and the double check lock won't work reliably. - */ -#if !defined(UMTX_STRONG_MEMORY_MODEL) -#define UMTX_STRONG_MEMORY_MODEL 0 +#ifndef UMTX_FULL_BARRIER +# if !ICU_USE_THREADS +# define UMTX_FULL_BARRIER +# elif U_HAVE_GCC_ATOMICS +# define UMTX_FULL_BARRIER __sync_synchronize(); +# elif defined(U_WINDOWS) +# define UMTX_FULL_BARRIER _ReadWriteBarrier(); +# elif defined(U_DARWIN) +# define UMTX_FULL_BARRIER OSMemoryBarrier(); +# else +# define UMTX_FULL_BARRIER \ + { \ + umtx_lock(NULL); \ + umtx_unlock(NULL); \ + } +# endif +#endif + +#ifndef UMTX_ACQUIRE_BARRIER +# define UMTX_ACQUIRE_BARRIER UMTX_FULL_BARRIER +#endif + +#ifndef UMTX_RELEASE_BARRIER +# define UMTX_RELEASE_BARRIER UMTX_FULL_BARRIER #endif /** * \def UMTX_CHECK - * Encapsulates a safe check of an expression + * Encapsulates a safe check of an expression * for use with double-checked lazy inititialization. - * On CPUs with weak memory models, this must use memory fence instructions - * or mutexes. + * Either memory barriers or mutexes are required, to prevent both the hardware + * and the compiler from reordering operations across the check. * The expression must involve only a _single_ variable, typically * a possibly null pointer or a boolean that indicates whether some service * is initialized or not. * The setting of the variable involved in the test must be the last step of * the initialization process. * - * * @internal */ -#if UMTX_STRONG_MEMORY_MODEL - #define UMTX_CHECK(pMutex, expression, result) \ - (result)=(expression) - -#else - -#define UMTX_CHECK(pMutex, expression, result) \ - umtx_lock(pMutex); \ - (result)=(expression); \ - umtx_unlock(pMutex) - -#endif + { \ + (result)=(expression); \ + UMTX_ACQUIRE_BARRIER; \ + } +/* + * TODO: Replace all uses of UMTX_CHECK and surrounding code + * with SimpleSingleton or TriStateSingleton, and remove UMTX_CHECK. + */ /* * Code within ICU that accesses shared static or global data should @@ -93,7 +124,7 @@ * an alternative C++ mutex API is defined in the file common/mutex.h */ -/* Lock a mutex. +/* Lock a mutex. * @param mutex The given mutex to be locked. Pass NULL to specify * the global ICU mutex. Recursive locks are an error * and may cause a deadlock on some platforms. @@ -128,8 +159,6 @@ U_CAPI void U_EXPORT2 umtx_init ( UMTX* mutex ); */ U_CAPI void U_EXPORT2 umtx_destroy( UMTX *mutex ); - - /* * Atomic Increment and Decrement of an int32_t value. * @@ -142,9 +171,5 @@ U_CAPI void U_EXPORT2 umtx_destroy( UMTX *mutex ); U_CAPI int32_t U_EXPORT2 umtx_atomic_inc(int32_t *); U_CAPI int32_t U_EXPORT2 umtx_atomic_dec(int32_t *); - #endif /*_CMUTEX*/ /*eof*/ - - - diff --git a/icu4c/source/common/unicode/platform.h.in b/icu4c/source/common/unicode/platform.h.in index 76543b0ec0..d21daf130d 100644 --- a/icu4c/source/common/unicode/platform.h.in +++ b/icu4c/source/common/unicode/platform.h.in @@ -151,11 +151,6 @@ #define ICU_USE_THREADS @ICU_USE_THREADS@ #endif -/* On strong memory model CPUs (e.g. x86 CPUs), we use a safe & quick double check lock. */ -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define UMTX_STRONG_MEMORY_MODEL 1 -#endif - #ifndef U_DEBUG #define U_DEBUG @ENABLE_DEBUG@ #endif diff --git a/icu4c/source/common/unicode/pwin32.h b/icu4c/source/common/unicode/pwin32.h index 1c04e869cb..32a9881daf 100644 --- a/icu4c/source/common/unicode/pwin32.h +++ b/icu4c/source/common/unicode/pwin32.h @@ -1,7 +1,7 @@ /* ****************************************************************************** * - * Copyright (C) 1997-2010, International Business Machines + * Copyright (C) 1997-2011, International Business Machines * Corporation and others. All Rights Reserved. * ****************************************************************************** @@ -190,15 +190,6 @@ typedef unsigned int uint32_t; #define UCLN_NO_AUTO_CLEANUP 1 #endif -/* On strong memory model CPUs (e.g. x86 CPUs), we use a safe & quick double check mutex lock. */ -/** -Microsoft can define _M_IX86, _M_AMD64 (before Visual Studio 8) or _M_X64 (starting in Visual Studio 8). -Intel can define _M_IX86 or _M_X64 -*/ -#if defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64) || (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) -#define UMTX_STRONG_MEMORY_MODEL 1 -#endif - /** Enable or disable debugging options **/ #ifndef U_DEBUG #ifdef _DEBUG