ICU-8161 Mutexes, add thread checker annotations, improve singleton implementation.
X-SVN-Rev: 29702
This commit is contained in:
parent
90412460c5
commit
040bb01ac4
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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*/
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user