]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/udata.cpp
ICU-57132.0.1.tar.gz
[apple/icu.git] / icuSources / common / udata.cpp
index f82b531c74d45b82b3289d2cea9195a833c67800..2d92fbf2acd48543522b0cda1b9b531721f3b067 100644 (file)
@@ -1,7 +1,7 @@
 /*
 ******************************************************************************
 *
 /*
 ******************************************************************************
 *
-*   Copyright (C) 1999-2013, International Business Machines
+*   Copyright (C) 1999-2016, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *
 ******************************************************************************
 *   Corporation and others.  All Rights Reserved.
 *
 ******************************************************************************
@@ -29,6 +29,7 @@ might have to #include some other header
 #include "charstr.h"
 #include "cmemory.h"
 #include "cstring.h"
 #include "charstr.h"
 #include "cmemory.h"
 #include "cstring.h"
+#include "mutex.h"
 #include "putilimp.h"
 #include "uassert.h"
 #include "ucln_cmn.h"
 #include "putilimp.h"
 #include "uassert.h"
 #include "ucln_cmn.h"
@@ -66,13 +67,13 @@ might have to #include some other header
 
 /* If you are excruciatingly bored turn this on .. */
 /* #define UDATA_DEBUG 1 */
 
 /* If you are excruciatingly bored turn this on .. */
 /* #define UDATA_DEBUG 1 */
+/* For debugging use of timezone data in a separate file */
+/* #define UDATA_TZFILES_DEBUG 1 */
 
 
-#if defined(UDATA_DEBUG)
+#if defined(UDATA_DEBUG) || defined(UDATA_TZFILES_DEBUG)
 #   include <stdio.h>
 #endif
 
 #   include <stdio.h>
 #endif
 
-#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
-
 U_NAMESPACE_USE
 
 /*
 U_NAMESPACE_USE
 
 /*
@@ -102,14 +103,15 @@ static UDataMemory *udata_findCachedData(const char *path);
  * that they really need, reducing the size of binaries that take advantage
  * of this.
  */
  * that they really need, reducing the size of binaries that take advantage
  * of this.
  */
-static UDataMemory *gCommonICUDataArray[10] = { NULL };
+static UDataMemory *gCommonICUDataArray[10] = { NULL };   // Access protected by icu global mutex.
 
 
-static UBool gHaveTriedToLoadCommonData = FALSE;  /* See extendICUData(). */
+static u_atomic_int32_t gHaveTriedToLoadCommonData = ATOMIC_INT32_T_INITIALIZER(0);  //  See extendICUData().
 
 static UHashtable  *gCommonDataCache = NULL;  /* Global hash table of opened ICU data files.  */
 static icu::UInitOnce gCommonDataCacheInitOnce = U_INITONCE_INITIALIZER;
 
 
 static UHashtable  *gCommonDataCache = NULL;  /* Global hash table of opened ICU data files.  */
 static icu::UInitOnce gCommonDataCacheInitOnce = U_INITONCE_INITIALIZER;
 
-static UDataFileAccess  gDataFileAccess = UDATA_DEFAULT_ACCESS;
+static UDataFileAccess  gDataFileAccess = UDATA_DEFAULT_ACCESS;  // Access not synchronized.
+                                                                 // Modifying is documented as thread-unsafe.
 
 static UBool U_CALLCONV
 udata_cleanup(void)
 
 static UBool U_CALLCONV
 udata_cleanup(void)
@@ -122,11 +124,11 @@ udata_cleanup(void)
     }
     gCommonDataCacheInitOnce.reset();
 
     }
     gCommonDataCacheInitOnce.reset();
 
-    for (i = 0; i < LENGTHOF(gCommonICUDataArray) && gCommonICUDataArray[i] != NULL; ++i) {
+    for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray) && gCommonICUDataArray[i] != NULL; ++i) {
         udata_close(gCommonICUDataArray[i]);
         gCommonICUDataArray[i] = NULL;
     }
         udata_close(gCommonICUDataArray[i]);
         gCommonICUDataArray[i] = NULL;
     }
-    gHaveTriedToLoadCommonData = FALSE;
+    gHaveTriedToLoadCommonData = 0;
 
     return TRUE;                   /* Everything was cleaned up */
 }
 
     return TRUE;                   /* Everything was cleaned up */
 }
@@ -141,14 +143,16 @@ findCommonICUDataByName(const char *inBasename)
     if (pData == NULL)
         return FALSE;
 
     if (pData == NULL)
         return FALSE;
 
-    for (i = 0; i < LENGTHOF(gCommonICUDataArray); ++i) {
-        if ((gCommonICUDataArray[i] != NULL) && (gCommonICUDataArray[i]->pHeader == pData->pHeader)) {
-            /* The data pointer is already in the array. */
-            found = TRUE;
-            break;
+    {
+        Mutex lock;
+        for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) {
+            if ((gCommonICUDataArray[i] != NULL) && (gCommonICUDataArray[i]->pHeader == pData->pHeader)) {
+                /* The data pointer is already in the array. */
+                found = TRUE;
+                break;
+            }
         }
     }
         }
     }
-
     return found;
 }
 
     return found;
 }
 
@@ -175,10 +179,9 @@ setCommonICUData(UDataMemory *pData,     /*  The new common data.  Belongs to ca
     /*    their locals.                                                              */
     UDatamemory_assign(newCommonData, pData);
     umtx_lock(NULL);
     /*    their locals.                                                              */
     UDatamemory_assign(newCommonData, pData);
     umtx_lock(NULL);
-    for (i = 0; i < LENGTHOF(gCommonICUDataArray); ++i) {
+    for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) {
         if (gCommonICUDataArray[i] == NULL) {
             gCommonICUDataArray[i] = newCommonData;
         if (gCommonICUDataArray[i] == NULL) {
             gCommonICUDataArray[i] = newCommonData;
-            ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup);
             didUpdate = TRUE;
             break;
         } else if (gCommonICUDataArray[i]->pHeader == pData->pHeader) {
             didUpdate = TRUE;
             break;
         } else if (gCommonICUDataArray[i]->pHeader == pData->pHeader) {
@@ -188,10 +191,12 @@ setCommonICUData(UDataMemory *pData,     /*  The new common data.  Belongs to ca
     }
     umtx_unlock(NULL);
 
     }
     umtx_unlock(NULL);
 
-    if (i == LENGTHOF(gCommonICUDataArray) && warn) {
+    if (i == UPRV_LENGTHOF(gCommonICUDataArray) && warn) {
         *pErr = U_USING_DEFAULT_WARNING;
     }
         *pErr = U_USING_DEFAULT_WARNING;
     }
-    if (!didUpdate) {
+    if (didUpdate) {
+        ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup);
+    } else {
         uprv_free(newCommonData);
     }
     return didUpdate;
         uprv_free(newCommonData);
     }
     return didUpdate;
@@ -661,10 +666,14 @@ openCommonData(const char *path,          /*  Path from OpenChoice?          */
     /* ??????? TODO revisit this */ 
     if (commonDataIndex >= 0) {
         /* "mini-cache" for common ICU data */
     /* ??????? TODO revisit this */ 
     if (commonDataIndex >= 0) {
         /* "mini-cache" for common ICU data */
-        if(commonDataIndex >= LENGTHOF(gCommonICUDataArray)) {
+        if(commonDataIndex >= UPRV_LENGTHOF(gCommonICUDataArray)) {
             return NULL;
         }
             return NULL;
         }
-        if(gCommonICUDataArray[commonDataIndex] == NULL) {
+        {
+            Mutex lock;
+            if(gCommonICUDataArray[commonDataIndex] != NULL) {
+                return gCommonICUDataArray[commonDataIndex];
+            }
             int32_t i;
             for(i = 0; i < commonDataIndex; ++i) {
                 if(gCommonICUDataArray[i]->pHeader == &U_ICUDATA_ENTRY_POINT) {
             int32_t i;
             for(i = 0; i < commonDataIndex; ++i) {
                 if(gCommonICUDataArray[i]->pHeader == &U_ICUDATA_ENTRY_POINT) {
@@ -672,23 +681,26 @@ openCommonData(const char *path,          /*  Path from OpenChoice?          */
                     return NULL;
                 }
             }
                     return NULL;
                 }
             }
+        }
 
 
-            /* Add the linked-in data to the list. */
-            /*
-             * This is where we would check and call weakly linked partial-data-library
-             * access functions.
-             */
-            /*
-            if (uprv_getICUData_collation) {
-                setCommonICUDataPointer(uprv_getICUData_collation(), FALSE, pErrorCode);
-            }
-            if (uprv_getICUData_conversion) {
-                setCommonICUDataPointer(uprv_getICUData_conversion(), FALSE, pErrorCode);
-            }
-            */
-            setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, FALSE, pErrorCode);
+        /* Add the linked-in data to the list. */
+        /*
+         * This is where we would check and call weakly linked partial-data-library
+         * access functions.
+         */
+        /*
+        if (uprv_getICUData_collation) {
+            setCommonICUDataPointer(uprv_getICUData_collation(), FALSE, pErrorCode);
+        }
+        if (uprv_getICUData_conversion) {
+            setCommonICUDataPointer(uprv_getICUData_conversion(), FALSE, pErrorCode);
+        }
+        */
+        setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, FALSE, pErrorCode);
+        {
+            Mutex lock;
+            return gCommonICUDataArray[commonDataIndex];
         }
         }
-        return gCommonICUDataArray[commonDataIndex];
     }
 
 
     }
 
 
@@ -796,7 +808,7 @@ static UBool extendICUData(UErrorCode *pErr)
     static UMutex extendICUDataMutex = U_MUTEX_INITIALIZER;
     umtx_lock(&extendICUDataMutex);
 #endif
     static UMutex extendICUDataMutex = U_MUTEX_INITIALIZER;
     umtx_lock(&extendICUDataMutex);
 #endif
-    if(!gHaveTriedToLoadCommonData) {
+    if(!umtx_loadAcquire(gHaveTriedToLoadCommonData)) {
         /* See if we can explicitly open a .dat file for the ICUData. */
         pData = openCommonData(
                    U_ICUDATA_NAME,            /*  "icudt20l" , for example.          */
         /* See if we can explicitly open a .dat file for the ICUData. */
         pData = openCommonData(
                    U_ICUDATA_NAME,            /*  "icudt20l" , for example.          */
@@ -821,7 +833,7 @@ static UBool extendICUData(UErrorCode *pErr)
                        pErr);             /*  setCommonICUData honors errors; NOP if error set    */
         }
 
                        pErr);             /*  setCommonICUData honors errors; NOP if error set    */
         }
 
-        gHaveTriedToLoadCommonData = TRUE;
+        umtx_storeRelease(gHaveTriedToLoadCommonData, 1);
     }
 
     didUpdate = findCommonICUDataByName(U_ICUDATA_NAME);  /* Return 'true' when a racing writes out the extended                        */
     }
 
     didUpdate = findCommonICUDataByName(U_ICUDATA_NAME);  /* Return 'true' when a racing writes out the extended                        */
@@ -1065,6 +1077,17 @@ static UDataMemory *doLoadFromCommonData(UBool isICUData, const char * /*pkgName
     }
 }
 
     }
 }
 
+/*
+ * Identify the Time Zone resources that are subject to special override data loading.
+ */
+static UBool isTimeZoneFile(const char *name, const char *type) {
+    return ((uprv_strcmp(type, "res") == 0) &&
+            (uprv_strcmp(name, "zoneinfo64") == 0 ||
+             uprv_strcmp(name, "timezoneTypes") == 0 ||
+             uprv_strcmp(name, "windowsZones") == 0 ||
+             uprv_strcmp(name, "metaZones") == 0));
+}
+
 /*
  *  A note on the ownership of Mapped Memory
  *
 /*
  *  A note on the ownership of Mapped Memory
  *
@@ -1233,15 +1256,83 @@ doOpenChoice(const char *path, const char *type, const char *name,
     /* End of dealing with a null basename */
     dataPath = u_getDataDirectory();
 
     /* End of dealing with a null basename */
     dataPath = u_getDataDirectory();
 
+    /****    Time zone individual files override  */
+    if (isTimeZoneFile(name, type) && isICUData) {
+        const char *tzFilesDir = u_getTimeZoneFilesDirectory(pErrorCode);
+        if (tzFilesDir[0] != 0) {
+#ifdef UDATA_DEBUG
+            fprintf(stderr, "Trying Time Zone Files directory = %s\n", tzFilesDir);
+#endif
+#ifdef UDATA_TZFILES_DEBUG
+            fprintf(stderr, "# dOC U_TIMEZONE_FILES_DIR:  %s\n", U_TIMEZONE_FILES_DIR);
+#endif
+
+#if defined(U_TIMEZONE_PACKAGE)
+            // make tztocEntryName, like tocEntryName but with our package name
+            UErrorCode tzpkgErrorCode = U_ZERO_ERROR;
+            CharString tztocPkgPath;
+            tztocPkgPath.append(tzFilesDir, tzpkgErrorCode);
+            tztocPkgPath.append(U_FILE_SEP_CHAR, tzpkgErrorCode).append(U_TIMEZONE_PACKAGE, tzpkgErrorCode);
+            CharString tztocEntryName;
+            tztocEntryName.append(U_TIMEZONE_PACKAGE, tzpkgErrorCode);
+            if(!treeName.isEmpty()) {
+                tztocEntryName.append(U_TREE_ENTRY_SEP_CHAR, tzpkgErrorCode).append(treeName, tzpkgErrorCode);
+            }
+            tztocEntryName.append(U_TREE_ENTRY_SEP_CHAR, tzpkgErrorCode).append(name, tzpkgErrorCode);
+            if(type!=NULL && *type!=0) {
+                tztocEntryName.append(".", tzpkgErrorCode).append(type, tzpkgErrorCode);
+            }
+#ifdef UDATA_TZFILES_DEBUG
+            fprintf(stderr, "# dOC tz pkg, doLoadFromCommonData start; U_TIMEZONE_PACKAGE: %s, tztocPkgPath.data(): %s, tztocEntryName.data(): %s, name: %s\n",
+                            U_TIMEZONE_PACKAGE, tztocPkgPath.data(), tztocEntryName.data(), name);
+#endif
+            retVal = doLoadFromCommonData(FALSE, "" /*ignored*/, "" /*ignored*/, "" /*ignored*/,
+                            tztocEntryName.data(),  // tocEntryName, like icutz44/zoneinfo64.res
+                            tztocPkgPath.data(),     // path =  path to pkg, like /usr/share/icu/icutz44l
+                            type, name, isAcceptable, context, &subErrorCode, &tzpkgErrorCode);
+#ifdef UDATA_TZFILES_DEBUG
+            fprintf(stderr, "# dOC tz pkg, doLoadFromCommonData end; status %d, retVal %p\n", tzpkgErrorCode, retVal);
+#endif
+            if(U_SUCCESS(tzpkgErrorCode) && retVal != NULL) {
+                return retVal;
+            }
+#endif /* defined(U_TIMEZONE_PACKAGE) */
+            // The following assumes any timezone resources in tzFilesDir are in individual .res files
+#ifdef UDATA_TZFILES_DEBUG
+            fprintf(stderr, "# dOC tz files, doLoadFromIndividualFiles start; tzFilesDir: %s, tocEntryPathSuffix: %s, name: %s\n",
+                            tzFilesDir, tocEntryPathSuffix, name);
+#endif
+            retVal = doLoadFromIndividualFiles(/* pkgName.data() */ "", tzFilesDir, tocEntryPathSuffix,
+                            /* path */ "", type, name, isAcceptable, context, &subErrorCode, pErrorCode);
+#ifdef UDATA_TZFILES_DEBUG
+            fprintf(stderr, "# dOC tz files, doLoadFromIndividualFiles end; status %d, retVal %p\n", *pErrorCode, retVal);
+#endif
+            if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
+                return retVal;
+            }
+        }
+    }
+
     /****    COMMON PACKAGE  - only if packages are first. */
     if(gDataFileAccess == UDATA_PACKAGES_FIRST) {
 #ifdef UDATA_DEBUG
         fprintf(stderr, "Trying packages (UDATA_PACKAGES_FIRST)\n");
 #endif
         /* #2 */
     /****    COMMON PACKAGE  - only if packages are first. */
     if(gDataFileAccess == UDATA_PACKAGES_FIRST) {
 #ifdef UDATA_DEBUG
         fprintf(stderr, "Trying packages (UDATA_PACKAGES_FIRST)\n");
 #endif
         /* #2 */
+#ifdef UDATA_TZFILES_DEBUG
+        if (isTimeZoneFile(name, type)) {
+            fprintf(stderr, "# dOC std common 1, doLoadFromCommonData start; U_TIMEZONE_PACKAGE: path: %s, tocEntryName.data(): %s, name: %s\n",
+                            path, tocEntryName.data(), name);
+        }
+#endif
         retVal = doLoadFromCommonData(isICUData, 
                             pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(),
                             path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
         retVal = doLoadFromCommonData(isICUData, 
                             pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(),
                             path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
+#ifdef UDATA_TZFILES_DEBUG
+        if (isTimeZoneFile(name, type)) {
+            fprintf(stderr, "# dOC std common 1, doLoadFromCommonData end; status %d, retVal %p\n", *pErrorCode, retVal);
+        }
+#endif
         if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
             return retVal;
         }
         if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
             return retVal;
         }
@@ -1255,8 +1346,19 @@ doOpenChoice(const char *path, const char *type, const char *name,
 #endif
         /* Check to make sure that there is a dataPath to iterate over */
         if ((dataPath && *dataPath) || !isICUData) {
 #endif
         /* Check to make sure that there is a dataPath to iterate over */
         if ((dataPath && *dataPath) || !isICUData) {
+#ifdef UDATA_TZFILES_DEBUG
+            if (isTimeZoneFile(name, type)) {
+                fprintf(stderr, "# dOC std indiv files, doLoadFromIndividualFiles start; dataPath: %s, tocEntryPathSuffix: %s, name: %s\n",
+                            dataPath, tocEntryPathSuffix, name);
+            }
+#endif
             retVal = doLoadFromIndividualFiles(pkgName.data(), dataPath, tocEntryPathSuffix,
                             path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
             retVal = doLoadFromIndividualFiles(pkgName.data(), dataPath, tocEntryPathSuffix,
                             path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
+#ifdef UDATA_TZFILES_DEBUG
+            if (isTimeZoneFile(name, type)) {
+                fprintf(stderr, "# dOC std indiv files, doLoadFromIndividualFiles end; status %d, retVal %p\n", *pErrorCode, retVal);
+            }
+#endif
             if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
                 return retVal;
             }
             if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
                 return retVal;
             }
@@ -1268,10 +1370,21 @@ doOpenChoice(const char *path, const char *type, const char *name,
        (gDataFileAccess==UDATA_FILES_FIRST)) {
 #ifdef UDATA_DEBUG
         fprintf(stderr, "Trying packages (UDATA_ONLY_PACKAGES || UDATA_FILES_FIRST)\n");
        (gDataFileAccess==UDATA_FILES_FIRST)) {
 #ifdef UDATA_DEBUG
         fprintf(stderr, "Trying packages (UDATA_ONLY_PACKAGES || UDATA_FILES_FIRST)\n");
+#endif
+#ifdef UDATA_TZFILES_DEBUG
+        if (isTimeZoneFile(name, type)) {
+            fprintf(stderr, "# dOC std common 2, doLoadFromCommonData start; U_TIMEZONE_PACKAGE: path: %s, tocEntryName.data(): %s, name: %s\n",
+                            path, tocEntryName.data(), name);
+        }
 #endif
         retVal = doLoadFromCommonData(isICUData,
                             pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(),
                             path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
 #endif
         retVal = doLoadFromCommonData(isICUData,
                             pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(),
                             path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
+#ifdef UDATA_TZFILES_DEBUG
+        if (isTimeZoneFile(name, type)) {
+            fprintf(stderr, "# dOC std common 2, doLoadFromCommonData end; status %d, retVal %p\n", *pErrorCode, retVal);
+        }
+#endif
         if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
             return retVal;
         }
         if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
             return retVal;
         }
@@ -1373,5 +1486,6 @@ udata_getInfo(UDataMemory *pData, UDataInfo *pInfo) {
 
 U_CAPI void U_EXPORT2 udata_setFileAccess(UDataFileAccess access, UErrorCode * /*status*/)
 {
 
 U_CAPI void U_EXPORT2 udata_setFileAccess(UDataFileAccess access, UErrorCode * /*status*/)
 {
+    // Note: this function is documented as not thread safe.
     gDataFileAccess = access;
 }
     gDataFileAccess = access;
 }