Changeset 32865


Ignore:
Timestamp:
11/19/12 10:02:46 (3 years ago)
Author:
tkeep
Message:

ticket:9737: Fix Locale::getDefault() and Locale::setDefault() so that the reference that getDefault() returns doesn't get freed unexpectedly due to race conditions.

Location:
icu/trunk/source/common
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • icu/trunk/source/common/locid.cpp

    r31105 r32865  
    11/* 
    22 ********************************************************************** 
    3  *   Copyright (C) 1997-2011, International Business Machines 
     3 *   Copyright (C) 1997-2012, International Business Machines 
    44 *   Corporation and others.  All Rights Reserved. 
    55 ********************************************************************** 
     
    3434#include "unicode/uloc.h" 
    3535#include "putilimp.h" 
     36#include "mutex.h" 
    3637#include "umutex.h" 
    3738#include "uassert.h" 
     
    4344 
    4445#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) 
     46 
     47U_CDECL_BEGIN 
     48static UBool U_CALLCONV locale_cleanup(void); 
     49U_CDECL_END 
     50 
     51U_NAMESPACE_BEGIN 
     52 
     53static void default_locale_initializer(UErrorCode& status); 
     54 
     55/** 
     56 * SingletonSpec is the specification for a singleton it contains 
     57 * the following information: 
     58 * <ul> 
     59 *   <li>The mutex that protects the singleton.</li> 
     60 *   <li>Pointer to the function that initializes the singleton.</li> 
     61 *   <li>Pointer to the singleton itself. It is used to determine if 
     62 *   the singleton has been initialized. The singleton is expected to 
     63 *   be a pointer. If the singleton is non-NULL, then it is 
     64 *   initialized.</li> 
     65 * </ul> 
     66 * <p> 
     67 * SingletonSpec does not extend UMemory because it is expected to be 
     68 * allocated in the data segment along with the singleton it specifies. 
     69 */ 
     70struct SingletonSpec { 
     71    UMutex mutex; 
     72    void (*initFunc)(UErrorCode& status); 
     73    void **singleton; 
     74}; 
     75 
     76/** 
     77 * SingletonLock protects a singleton like a mutex would. Note that 
     78 * singletonSpec determines what singleton gets protected. Use like this: 
     79 * <pre> 
     80 * { 
     81 *     SingletonLock lock(singletonSpec, status); 
     82 *     if (U_FAILURE(status)) { 
     83 *         // Unsafe to use singleton. 
     84 *         return 
     85 *     } 
     86 *     // Singleton is initialized, use normally. 
     87 * } 
     88 * // Lock on singleton released. 
     89 */  
     90class SingletonLock : UMemory { 
     91public: 
     92    SingletonLock(SingletonSpec& spec, UErrorCode& status) : _mutex(&spec.mutex) { 
     93        if (*spec.singleton == NULL) { 
     94            spec.initFunc(status); 
     95        } 
     96    } 
     97    ~SingletonLock() { } 
     98private: 
     99    Mutex _mutex; 
     100}; 
     101 
     102static Locale *gLocaleCache = NULL; 
     103 
     104// gDefaultLocaleSpec covers gDefaultLocalesHashT and gDefaultLocale 
     105// gDefaultLocaleSpec does not initialize gDefaultLocale though 
     106static UHashtable *gDefaultLocalesHashT = NULL; 
     107static Locale *gDefaultLocale = NULL; 
     108static SingletonSpec gDefaultLocaleSpec = { 
     109    U_MUTEX_INITIALIZER, 
     110    default_locale_initializer, 
     111    (void**) &gDefaultLocalesHashT 
     112}; 
     113 
     114U_NAMESPACE_END 
    45115 
    46116typedef enum ELocalePos { 
     
    78148            UErrorCode *status); 
    79149 
    80 static icu::Locale *gLocaleCache = NULL; 
    81 static icu::Locale *gDefaultLocale = NULL; 
    82 static UHashtable *gDefaultLocalesHashT = NULL; 
    83  
    84150U_CDECL_BEGIN 
    85151// 
     
    103169        uhash_close(gDefaultLocalesHashT);   // Automatically deletes all elements, using deleter func. 
    104170        gDefaultLocalesHashT = NULL; 
    105     } 
    106     else if (gDefaultLocale) { 
    107         // The cache wasn't created, and only one default locale was created. 
    108         delete gDefaultLocale; 
    109     } 
    110     gDefaultLocale = NULL; 
     171        gDefaultLocale = NULL; 
     172    } 
    111173 
    112174    return TRUE; 
     
    115177 
    116178U_NAMESPACE_BEGIN 
    117 // 
    118 //  locale_set_default_internal. 
    119 // 
    120 void locale_set_default_internal(const char *id) 
    121 { 
    122     UErrorCode   status = U_ZERO_ERROR; 
     179 
     180// default_locale_initializer initializes gDefaultLocalesHashT 
     181static void default_locale_initializer(UErrorCode& status) { 
     182    gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 
     183    if (U_FAILURE(status)) { 
     184        return; 
     185    } 
     186    uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale); 
     187    ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); 
     188} 
     189 
     190void locale_set_default_internal(const char *id, UErrorCode& status) { 
    123191    UBool canonicalize = FALSE; 
    124192 
     
    128196    //    the current ICU default locale.) 
    129197    if (id == NULL) { 
    130         umtx_lock(NULL); 
    131198        id = uprv_getDefaultLocaleID(); 
    132         umtx_unlock(NULL); 
    133199        canonicalize = TRUE; // always canonicalize host ID 
    134200    } 
    135201 
    136     // put the locale id into a canonical form, 
    137     //   in preparation for looking up this locale in the hash table of 
    138     //   already-created locale objects. 
    139     // 
    140     status = U_ZERO_ERROR; 
    141202    char localeNameBuf[512]; 
    142203 
     
    149210                                                 //   a long name filling the buffer. 
    150211                                                 //   (long names are truncated.) 
    151  
    152     // Lazy creation of the hash table itself, if needed. 
    153     UBool isOnlyLocale; 
    154     UMTX_CHECK(NULL, (gDefaultLocale == NULL), isOnlyLocale); 
    155     if (isOnlyLocale) { 
    156         // We haven't seen this locale id before. 
    157         // Create a new Locale object for it. 
    158         Locale *newFirstDefault = new Locale(Locale::eBOGUS); 
    159         if (newFirstDefault == NULL) { 
    160             // No way to report errors from here. 
     212                                                 // 
     213    if (U_FAILURE(status)) { 
     214        return; 
     215    } 
     216    Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf); 
     217    if (newDefault == NULL) { 
     218        newDefault = new Locale(Locale::eBOGUS); 
     219        if (newDefault == NULL) { 
     220            status = U_MEMORY_ALLOCATION_ERROR; 
    161221            return; 
    162222        } 
    163         newFirstDefault->init(localeNameBuf, FALSE); 
    164         umtx_lock(NULL); 
    165         if (gDefaultLocale == NULL) { 
    166             gDefaultLocale = newFirstDefault;  // Assignment to gDefaultLocale must happen inside mutex 
    167             newFirstDefault = NULL; 
    168             ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); 
    169         } 
    170         // Else some other thread raced us through here, and set the new Locale. 
    171         // Use the hash table next. 
    172         umtx_unlock(NULL); 
    173         if (newFirstDefault == NULL) { 
    174             // We were successful in setting the locale, and we were the first one to set it. 
    175             return; 
    176         } 
    177         // else start using the hash table. 
    178     } 
    179  
    180     // Lazy creation of the hash table itself, if needed. 
    181     UBool hashTableNeedsInit; 
    182     UMTX_CHECK(NULL, (gDefaultLocalesHashT == NULL), hashTableNeedsInit); 
    183     if (hashTableNeedsInit) { 
    184         status = U_ZERO_ERROR; 
    185         UHashtable *tHashTable = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 
     223        newDefault->init(localeNameBuf, FALSE); 
     224        uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status); 
    186225        if (U_FAILURE(status)) { 
    187226            return; 
    188227        } 
    189         uhash_setValueDeleter(tHashTable, deleteLocale); 
    190         umtx_lock(NULL); 
    191         if (gDefaultLocalesHashT == NULL) { 
    192             gDefaultLocalesHashT = tHashTable; 
    193             ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); 
    194         } else { 
    195             uhash_close(tHashTable); 
    196             hashTableNeedsInit = FALSE; 
    197         } 
    198         umtx_unlock(NULL); 
    199     } 
    200  
    201     // Hash table lookup, key is the locale full name 
    202     umtx_lock(NULL); 
    203     Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf); 
    204     if (newDefault != NULL) { 
    205         // We have the requested locale in the hash table already. 
    206         // Just set it as default.  Inside the mutex lock, for those troublesome processors. 
    207         gDefaultLocale = newDefault; 
    208         umtx_unlock(NULL); 
    209     } else { 
    210         umtx_unlock(NULL); 
    211         // We haven't seen this locale id before. 
    212         // Create a new Locale object for it. 
    213         newDefault = new Locale(Locale::eBOGUS); 
    214         if (newDefault == NULL) { 
    215             // No way to report errors from here. 
    216             return; 
    217         } 
    218         newDefault->init(localeNameBuf, FALSE); 
    219  
    220         // Add newly created Locale to the hash table of default Locales 
    221         const char *key = newDefault->getName(); 
    222         U_ASSERT(uprv_strcmp(key, localeNameBuf) == 0); 
    223         umtx_lock(NULL); 
    224         Locale *hashTableVal = (Locale *)uhash_get(gDefaultLocalesHashT, key); 
    225         if (hashTableVal == NULL) { 
    226             if (hashTableNeedsInit) { 
    227                 // This is the second request to set the locale. 
    228                 // Cache the first one. 
    229                 uhash_put(gDefaultLocalesHashT, (void *)gDefaultLocale->getName(), gDefaultLocale, &status); 
    230             } 
    231             uhash_put(gDefaultLocalesHashT, (void *)key, newDefault, &status); 
    232             gDefaultLocale = newDefault; 
    233             // ignore errors from hash table insert.  (Couldn't do anything anyway) 
    234             // We can still set the default Locale, 
    235             //  it just wont be cached, and will eventually leak. 
    236         } else { 
    237             // Some other thread raced us through here, and got the new Locale 
    238             //   into the hash table before us.  Use that one. 
    239             gDefaultLocale = hashTableVal;  // Assignment to gDefaultLocale must happen inside mutex 
    240             delete newDefault; 
    241         } 
    242         umtx_unlock(NULL); 
    243     } 
    244 } 
     228    } 
     229    gDefaultLocale = newDefault; 
     230} 
     231 
    245232U_NAMESPACE_END 
    246233 
     
    250237{ 
    251238    U_NAMESPACE_USE 
    252     locale_set_default_internal(id); 
     239    UErrorCode status = U_ZERO_ERROR; 
     240    { 
     241        SingletonLock lock(gDefaultLocaleSpec, status); 
     242        if (!U_FAILURE(status)) { 
     243            locale_set_default_internal(id, status); 
     244        } 
     245    } 
    253246} 
    254247/* end */ 
     
    664657Locale::getDefault() 
    665658{ 
    666     const Locale *retLocale; 
    667     UMTX_CHECK(NULL, gDefaultLocale, retLocale); 
    668     if (retLocale == NULL) { 
    669         locale_set_default_internal(NULL); 
    670         umtx_lock(NULL); 
    671         // Need a mutex  in case some other thread set a new 
    672         // default inbetween when we set and when we get the new default.  For 
    673         // processors with weak memory coherency, we might not otherwise see all 
    674         // of the newly created new default locale. 
    675         retLocale = gDefaultLocale; 
    676         umtx_unlock(NULL); 
    677     } 
    678     return *retLocale; 
     659    UErrorCode status = U_ZERO_ERROR; 
     660    { 
     661        SingletonLock lock(gDefaultLocaleSpec, status); 
     662        if (!U_FAILURE(status)) { 
     663           if (gDefaultLocale == NULL) { 
     664               locale_set_default_internal(NULL, status); 
     665           } 
     666        } 
     667        return *gDefaultLocale; 
     668    } 
    679669} 
    680670 
     
    693683     */ 
    694684    const char *localeID = newLocale.getName(); 
    695     locale_set_default_internal(localeID); 
     685    { 
     686        SingletonLock lock(gDefaultLocaleSpec, status); 
     687        if (!U_FAILURE(status)) { 
     688            locale_set_default_internal(localeID, status); 
     689        } 
     690    } 
    696691} 
    697692 
  • icu/trunk/source/common/unicode/locid.h

    r32372 r32865  
    740740     * @internal 
    741741     */ 
    742     friend void locale_set_default_internal(const char *); 
     742    friend void locale_set_default_internal(const char *, UErrorCode& status); 
    743743}; 
    744744 
Note: See TracChangeset for help on using the changeset viewer.