/*
*******************************************************************************
*
-* Copyright (C) 2008-2010, International Business Machines
+* Copyright (C) 2008-2011, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
#include "unicode/utypes.h"
#include "mutex.h"
+#include "uassert.h"
U_NAMESPACE_BEGIN
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;
+ }
+
+ // 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 {
- instance=instantiator(context, errorCode);
- Mutex mutex;
- if(fInstance==NULL && U_SUCCESS(errorCode)) {
- fInstance=instance;
- } else {
- duplicate=instance;
- }
- return fInstance;
+ 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) {
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
+ // 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;
- } 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;
- }
+ }
+
+ // 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;
- return fInstance;
+ 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