1 // Copyright (C) 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * 6 * Copyright (C) 1997-2016, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 * 9 ****************************************************************************** 10 * 11 * File umutex.cpp 12 * 13 * Modification History: 14 * 15 * Date Name Description 16 * 04/02/97 aliu Creation. 17 * 04/07/99 srl updated 18 * 05/13/99 stephen Changed to umutex (from cmutex). 19 * 11/22/99 aliu Make non-global mutex autoinitialize [j151] 20 ****************************************************************************** 21 */ 22 23 #include "umutex.h" 24 25 #include "unicode/utypes.h" 26 #include "uassert.h" 27 #include "cmemory.h" 28 29 30 // The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer. 31 static UMutex globalMutex = U_MUTEX_INITIALIZER; 32 33 /* 34 * ICU Mutex wrappers. Wrap operating system mutexes, giving the rest of ICU a 35 * platform independent set of mutex operations. For internal ICU use only. 36 */ 37 38 #if defined(U_USER_MUTEX_CPP) 39 // Build time user mutex hook: #include "U_USER_MUTEX_CPP" 40 #include U_MUTEX_XSTR(U_USER_MUTEX_CPP) 41 42 #elif U_PLATFORM_USES_ONLY_WIN32_API 43 44 #if defined U_NO_PLATFORM_ATOMICS 45 #error ICU on Win32 requires support for low level atomic operations. 46 // Visual Studio, gcc, clang are OK. Shouldn't get here. 47 #endif 48 49 50 // This function is called when a test of a UInitOnce::fState reveals that 51 // initialization has not completed, that we either need to call the 52 // function on this thread, or wait for some other thread to complete. 53 // 54 // The actual call to the init function is made inline by template code 55 // that knows the C++ types involved. This function returns TRUE if 56 // the caller needs to call the Init function. 57 // 58 59 U_NAMESPACE_BEGIN 60 61 U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) { 62 for (;;) { 63 int32_t previousState = InterlockedCompareExchange( 64 (LONG volatile *) // this is the type given in the API doc for this function. 65 &uio.fState, // Destination 66 1, // Exchange Value 67 0); // Compare value 68 69 if (previousState == 0) { 70 return true; // Caller will next call the init function. 71 // Current state == 1. 72 } else if (previousState == 2) { 73 // Another thread already completed the initialization. 74 // We can simply return FALSE, indicating no 75 // further action is needed by the caller. 76 return FALSE; 77 } else { 78 // Another thread is currently running the initialization. 79 // Wait until it completes. 80 do { 81 Sleep(1); 82 previousState = umtx_loadAcquire(uio.fState); 83 } while (previousState == 1); 84 } 85 } 86 } 87 88 // This function is called by the thread that ran an initialization function, 89 // just after completing the function. 90 91 U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) { 92 umtx_storeRelease(uio.fState, 2); 93 } 94 95 U_NAMESPACE_END 96 97 static void winMutexInit(CRITICAL_SECTION *cs) { 98 InitializeCriticalSection(cs); 99 return; 100 } 101 102 U_CAPI void U_EXPORT2 103 umtx_lock(UMutex *mutex) { 104 if (mutex == NULL) { 105 mutex = &globalMutex; 106 } 107 CRITICAL_SECTION *cs = &mutex->fCS; 108 umtx_initOnce(mutex->fInitOnce, winMutexInit, cs); 109 EnterCriticalSection(cs); 110 } 111 112 U_CAPI void U_EXPORT2 113 umtx_unlock(UMutex* mutex) 114 { 115 if (mutex == NULL) { 116 mutex = &globalMutex; 117 } 118 LeaveCriticalSection(&mutex->fCS); 119 } 120 121 122 U_CAPI void U_EXPORT2 123 umtx_condBroadcast(UConditionVar *condition) { 124 // We require that the associated mutex be held by the caller, 125 // so access to fWaitCount is protected and safe. No other thread can 126 // call condWait() while we are here. 127 if (condition->fWaitCount == 0) { 128 return; 129 } 130 ResetEvent(condition->fExitGate); 131 SetEvent(condition->fEntryGate); 132 } 133 134 U_CAPI void U_EXPORT2 135 umtx_condSignal(UConditionVar *condition) { 136 // Function not implemented. There is no immediate requirement from ICU to have it. 137 // Once ICU drops support for Windows XP and Server 2003, ICU Condition Variables will be 138 // changed to be thin wrappers on native Windows CONDITION_VARIABLEs, and this function 139 // becomes trivial to provide. 140 U_ASSERT(FALSE); 141 } 142 143 U_CAPI void U_EXPORT2 144 umtx_condWait(UConditionVar *condition, UMutex *mutex) { 145 if (condition->fEntryGate == NULL) { 146 // Note: because the associated mutex must be locked when calling 147 // wait, we know that there can not be multiple threads 148 // running here with the same condition variable. 149 // Meaning that lazy initialization is safe. 150 U_ASSERT(condition->fExitGate == NULL); 151 condition->fEntryGate = CreateEvent(NULL, // Security Attributes 152 TRUE, // Manual Reset 153 FALSE, // Initially reset 154 NULL); // Name. 155 U_ASSERT(condition->fEntryGate != NULL); 156 condition->fExitGate = CreateEvent(NULL, TRUE, TRUE, NULL); 157 U_ASSERT(condition->fExitGate != NULL); 158 } 159 160 condition->fWaitCount++; 161 umtx_unlock(mutex); 162 WaitForSingleObject(condition->fEntryGate, INFINITE); 163 umtx_lock(mutex); 164 condition->fWaitCount--; 165 if (condition->fWaitCount == 0) { 166 // All threads that were waiting at the entry gate have woken up 167 // and moved through. Shut the entry gate and open the exit gate. 168 ResetEvent(condition->fEntryGate); 169 SetEvent(condition->fExitGate); 170 } else { 171 umtx_unlock(mutex); 172 WaitForSingleObject(condition->fExitGate, INFINITE); 173 umtx_lock(mutex); 174 } 175 } 176 177 178 #elif U_PLATFORM_IMPLEMENTS_POSIX 179 180 //------------------------------------------------------------------------------------------- 181 // 182 // POSIX specific definitions 183 // 184 //------------------------------------------------------------------------------------------- 185 186 # include <pthread.h> 187 188 // Each UMutex consists of a pthread_mutex_t. 189 // All are statically initialized and ready for use. 190 // There is no runtime mutex initialization code needed. 191 192 U_CAPI void U_EXPORT2 193 umtx_lock(UMutex *mutex) { 194 if (mutex == NULL) { 195 mutex = &globalMutex; 196 } 197 int sysErr = pthread_mutex_lock(&mutex->fMutex); 198 (void)sysErr; // Suppress unused variable warnings. 199 U_ASSERT(sysErr == 0); 200 } 201 202 203 U_CAPI void U_EXPORT2 204 umtx_unlock(UMutex* mutex) 205 { 206 if (mutex == NULL) { 207 mutex = &globalMutex; 208 } 209 int sysErr = pthread_mutex_unlock(&mutex->fMutex); 210 (void)sysErr; // Suppress unused variable warnings. 211 U_ASSERT(sysErr == 0); 212 } 213 214 215 U_CAPI void U_EXPORT2 216 umtx_condWait(UConditionVar *cond, UMutex *mutex) { 217 if (mutex == NULL) { 218 mutex = &globalMutex; 219 } 220 int sysErr = pthread_cond_wait(&cond->fCondition, &mutex->fMutex); 221 (void)sysErr; 222 U_ASSERT(sysErr == 0); 223 } 224 225 U_CAPI void U_EXPORT2 226 umtx_condBroadcast(UConditionVar *cond) { 227 int sysErr = pthread_cond_broadcast(&cond->fCondition); 228 (void)sysErr; 229 U_ASSERT(sysErr == 0); 230 } 231 232 U_CAPI void U_EXPORT2 233 umtx_condSignal(UConditionVar *cond) { 234 int sysErr = pthread_cond_signal(&cond->fCondition); 235 (void)sysErr; 236 U_ASSERT(sysErr == 0); 237 } 238 239 240 241 U_NAMESPACE_BEGIN 242 243 static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER; 244 static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER; 245 246 247 // This function is called when a test of a UInitOnce::fState reveals that 248 // initialization has not completed, that we either need to call the 249 // function on this thread, or wait for some other thread to complete. 250 // 251 // The actual call to the init function is made inline by template code 252 // that knows the C++ types involved. This function returns TRUE if 253 // the caller needs to call the Init function. 254 // 255 U_COMMON_API UBool U_EXPORT2 256 umtx_initImplPreInit(UInitOnce &uio) { 257 pthread_mutex_lock(&initMutex); 258 int32_t state = uio.fState; 259 if (state == 0) { 260 umtx_storeRelease(uio.fState, 1); 261 pthread_mutex_unlock(&initMutex); 262 return TRUE; // Caller will next call the init function. 263 } else { 264 while (uio.fState == 1) { 265 // Another thread is currently running the initialization. 266 // Wait until it completes. 267 pthread_cond_wait(&initCondition, &initMutex); 268 } 269 pthread_mutex_unlock(&initMutex); 270 U_ASSERT(uio.fState == 2); 271 return FALSE; 272 } 273 } 274 275 276 277 // This function is called by the thread that ran an initialization function, 278 // just after completing the function. 279 // Some threads may be waiting on the condition, requiring the broadcast wakeup. 280 // Some threads may be racing to test the fState variable outside of the mutex, 281 // requiring the use of store/release when changing its value. 282 283 U_COMMON_API void U_EXPORT2 284 umtx_initImplPostInit(UInitOnce &uio) { 285 pthread_mutex_lock(&initMutex); 286 umtx_storeRelease(uio.fState, 2); 287 pthread_cond_broadcast(&initCondition); 288 pthread_mutex_unlock(&initMutex); 289 } 290 291 U_NAMESPACE_END 292 293 // End of POSIX specific umutex implementation. 294 295 #else // Platform #define chain. 296 297 #error Unknown Platform 298 299 #endif // Platform #define chain. 300 301 302 //------------------------------------------------------------------------------- 303 // 304 // Atomic Operations, out-of-line versions. 305 // These are conditional, only defined if better versions 306 // were not available for the platform. 307 // 308 // These versions are platform neutral. 309 // 310 //-------------------------------------------------------------------------------- 311 312 #if defined U_NO_PLATFORM_ATOMICS 313 static UMutex gIncDecMutex = U_MUTEX_INITIALIZER; 314 315 U_NAMESPACE_BEGIN 316 317 U_COMMON_API int32_t U_EXPORT2 318 umtx_atomic_inc(u_atomic_int32_t *p) { 319 int32_t retVal; 320 umtx_lock(&gIncDecMutex); 321 retVal = ++(*p); 322 umtx_unlock(&gIncDecMutex); 323 return retVal; 324 } 325 326 327 U_COMMON_API int32_t U_EXPORT2 328 umtx_atomic_dec(u_atomic_int32_t *p) { 329 int32_t retVal; 330 umtx_lock(&gIncDecMutex); 331 retVal = --(*p); 332 umtx_unlock(&gIncDecMutex); 333 return retVal; 334 } 335 336 U_COMMON_API int32_t U_EXPORT2 337 umtx_loadAcquire(u_atomic_int32_t &var) { 338 umtx_lock(&gIncDecMutex); 339 int32_t val = var; 340 umtx_unlock(&gIncDecMutex); 341 return val; 342 } 343 344 U_COMMON_API void U_EXPORT2 345 umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 346 umtx_lock(&gIncDecMutex); 347 var = val; 348 umtx_unlock(&gIncDecMutex); 349 } 350 351 U_NAMESPACE_END 352 #endif 353 354 //-------------------------------------------------------------------------- 355 // 356 // Deprecated functions for setting user mutexes. 357 // 358 //-------------------------------------------------------------------------- 359 360 U_DEPRECATED void U_EXPORT2 361 u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, 362 UMtxFn *, UMtxFn *, UErrorCode *status) { 363 if (U_SUCCESS(*status)) { 364 *status = U_UNSUPPORTED_ERROR; 365 } 366 return; 367 } 368 369 370 371 U_DEPRECATED void U_EXPORT2 372 u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, 373 UErrorCode *status) { 374 if (U_SUCCESS(*status)) { 375 *status = U_UNSUPPORTED_ERROR; 376 } 377 return; 378 } 379