ICU-8161 Mutexes, add thread checker annotations, improve singleton implementation.

X-SVN-Rev: 29702
This commit is contained in:
Andy Heninger 2011-03-29 00:47:41 +00:00
parent 90412460c5
commit 040bb01ac4
5 changed files with 148 additions and 89 deletions

View File

@ -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

View File

@ -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.
*/

View File

@ -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 <intrin.h>
#endif
#if defined(U_DARWIN)
# include <libkern/OSAtomic.h>
#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*/

View File

@ -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

View File

@ -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