]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/mutex.cpp
ICU-491.11.2.tar.gz
[apple/icu.git] / icuSources / common / mutex.cpp
index 8da3c7e0f675ecb37069df0606989ce3b374404b..e1e502d4ad90974fbf87e0b47e4dae2a814c4927 100644 (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.
 *
 *******************************************************************************
 *   Corporation and others.  All Rights Reserved.
 *
 *******************************************************************************
@@ -13,6 +13,7 @@
 
 #include "unicode/utypes.h"
 #include "mutex.h"
 
 #include "unicode/utypes.h"
 #include "mutex.h"
+#include "uassert.h"
 
 U_NAMESPACE_BEGIN
 
 
 U_NAMESPACE_BEGIN
 
@@ -23,22 +24,49 @@ void *SimpleSingleton::getInstance(InstantiatorFn *instantiator, const void *con
     if(U_FAILURE(errorCode)) {
         return NULL;
     }
     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;
     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 {
     } 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) {
 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;
     }
     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;
         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;
         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;
 }
 
 void TriStateSingleton::reset() {
     fInstance=NULL;
     fErrorCode=U_ZERO_ERROR;
-    fHaveInstance=0;
 }
 
 #if UCONFIG_NO_SERVICE
 }
 
 #if UCONFIG_NO_SERVICE