• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2  ******************************************************************************
3  *
4  *   Copyright (C) 1997-2012, International Business Machines
5  *   Corporation and others.  All Rights Reserved.
6  *
7  ******************************************************************************
8  *
9  * File umutex.cpp
10  *
11  * Modification History:
12  *
13  *   Date        Name        Description
14  *   04/02/97    aliu        Creation.
15  *   04/07/99    srl         updated
16  *   05/13/99    stephen     Changed to umutex (from cmutex).
17  *   11/22/99    aliu        Make non-global mutex autoinitialize [j151]
18  ******************************************************************************
19  */
20  
21  #include "unicode/utypes.h"
22  #include "uassert.h"
23  #include "ucln_cmn.h"
24  
25  /*
26   * ICU Mutex wrappers.  Wrap operating system mutexes, giving the rest of ICU a
27   * platform independent set of mutex operations.  For internal ICU use only.
28   */
29  
30  #if U_PLATFORM_HAS_WIN32_API
31      /* Prefer native Windows APIs even if POSIX is implemented (i.e., on Cygwin). */
32  #   undef POSIX
33  #elif U_PLATFORM_IMPLEMENTS_POSIX
34  #   define POSIX
35  #else
36  #   undef POSIX
37  #endif
38  
39  #if defined(POSIX)
40  # include <pthread.h> /* must be first, so that we get the multithread versions of things. */
41  #endif /* POSIX */
42  
43  #if U_PLATFORM_HAS_WIN32_API
44  # define WIN32_LEAN_AND_MEAN
45  # define VC_EXTRALEAN
46  # define NOUSER
47  # define NOSERVICE
48  # define NOIME
49  # define NOMCX
50  # include <windows.h>
51  #endif
52  
53  #include "umutex.h"
54  #include "cmemory.h"
55  
56  #if U_PLATFORM_HAS_WIN32_API
57  #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
58              InterlockedCompareExchangePointer(dest, newval, oldval)
59  
60  #elif defined(POSIX)
61  #if (U_HAVE_GCC_ATOMICS == 1)
62  #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
63              __sync_val_compare_and_swap(dest, oldval, newval)
64  #else
65  #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
66              mutexed_compare_and_swap(dest, newval, oldval)
67  #endif
68  
69  #else
70  // Unknown platform.  Note that user can still set mutex functions at run time.
71  #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
72              mutexed_compare_and_swap(dest, newval, oldval)
73  #endif
74  
75  static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval);
76  
77  // The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
78  static UMutex   globalMutex = U_MUTEX_INITIALIZER;
79  
80  // Implementation mutex. Used for compare & swap when no intrinsic is available, and
81  // for safe initialization of user defined mutexes.
82  static UMutex   implMutex = U_MUTEX_INITIALIZER;
83  
84  // List of all user mutexes that have been initialized.
85  // Used to allow us to destroy them when cleaning up ICU.
86  // Normal platform mutexes are not kept track of in this way - they survive until the process is shut down.
87  // Normal platfrom mutexes don't allocate storage, so not cleaning them up won't trigger memory leak complaints.
88  //
89  // Note: putting this list in allocated memory would be awkward to arrange, because memory allocations
90  //       are used as a flag to indicate that ICU has been initialized, and setting other ICU
91  //       override functions will no longer work.
92  //
93  static const int MUTEX_LIST_LIMIT = 100;
94  static UMutex *gMutexList[MUTEX_LIST_LIMIT];
95  static int gMutexListSize = 0;
96  
97  
98  /*
99   *  User mutex implementation functions.  If non-null, call back to these rather than
100   *  directly using the system (Posix or Windows) APIs.  See u_setMutexFunctions().
101   *    (declarations are in uclean.h)
102   */
103  static UMtxInitFn    *pMutexInitFn    = NULL;
104  static UMtxFn        *pMutexDestroyFn = NULL;
105  static UMtxFn        *pMutexLockFn    = NULL;
106  static UMtxFn        *pMutexUnlockFn  = NULL;
107  static const void    *gMutexContext   = NULL;
108  
109  
110  // Clean up (undo) the effects of u_setMutexFunctions().
111  //
usrMutexCleanup()112  static void usrMutexCleanup() {
113      if (pMutexDestroyFn != NULL) {
114          for (int i = 0; i < gMutexListSize; i++) {
115              UMutex *m = gMutexList[i];
116              U_ASSERT(m->fInitialized);
117              (*pMutexDestroyFn)(gMutexContext, &m->fUserMutex);
118              m->fInitialized = FALSE;
119          }
120          (*pMutexDestroyFn)(gMutexContext, &globalMutex.fUserMutex);
121          (*pMutexDestroyFn)(gMutexContext, &implMutex.fUserMutex);
122      }
123      gMutexListSize  = 0;
124      pMutexInitFn    = NULL;
125      pMutexDestroyFn = NULL;
126      pMutexLockFn    = NULL;
127      pMutexUnlockFn  = NULL;
128      gMutexContext   = NULL;
129  }
130  
131  
132  /*
133   * User mutex lock.
134   *
135   * User mutexes need to be initialized before they can be used. We use the impl mutex
136   * to synchronize the initialization check. This could be sped up on platforms that
137   * support alternate ways to safely check the initialization flag.
138   *
139   */
usrMutexLock(UMutex * mutex)140  static void usrMutexLock(UMutex *mutex) {
141      UErrorCode status = U_ZERO_ERROR;
142      if (!(mutex == &implMutex || mutex == &globalMutex)) {
143          umtx_lock(&implMutex);
144          if (!mutex->fInitialized) {
145              (*pMutexInitFn)(gMutexContext, &mutex->fUserMutex, &status);
146              U_ASSERT(U_SUCCESS(status));
147              mutex->fInitialized = TRUE;
148              U_ASSERT(gMutexListSize < MUTEX_LIST_LIMIT);
149              if (gMutexListSize < MUTEX_LIST_LIMIT) {
150                  gMutexList[gMutexListSize] = mutex;
151                  ++gMutexListSize;
152              }
153          }
154          umtx_unlock(&implMutex);
155      }
156      (*pMutexLockFn)(gMutexContext, &mutex->fUserMutex);
157  }
158  
159  
160  
161  #if defined(POSIX)
162  
163  //
164  // POSIX implementation of UMutex.
165  //
166  // Each UMutex has a corresponding pthread_mutex_t.
167  // All are statically initialized and ready for use.
168  // There is no runtime mutex initialization code needed.
169  
170  U_CAPI void  U_EXPORT2
umtx_lock(UMutex * mutex)171  umtx_lock(UMutex *mutex) {
172      if (mutex == NULL) {
173          mutex = &globalMutex;
174      }
175      if (pMutexLockFn) {
176          usrMutexLock(mutex);
177      } else {
178          #if U_DEBUG
179              // #if to avoid unused variable warnings in non-debug builds.
180              int sysErr = pthread_mutex_lock(&mutex->fMutex);
181              U_ASSERT(sysErr == 0);
182          #else
183              pthread_mutex_lock(&mutex->fMutex);
184          #endif
185      }
186  }
187  
188  
189  U_CAPI void  U_EXPORT2
umtx_unlock(UMutex * mutex)190  umtx_unlock(UMutex* mutex)
191  {
192      if (mutex == NULL) {
193          mutex = &globalMutex;
194      }
195      if (pMutexUnlockFn) {
196          (*pMutexUnlockFn)(gMutexContext, &mutex->fUserMutex);
197      } else {
198          #if U_DEBUG
199              // #if to avoid unused variable warnings in non-debug builds.
200              int sysErr = pthread_mutex_unlock(&mutex->fMutex);
201              U_ASSERT(sysErr == 0);
202          #else
203              pthread_mutex_unlock(&mutex->fMutex);
204          #endif
205      }
206  }
207  
208  #elif U_PLATFORM_HAS_WIN32_API
209  //
210  // Windows implementation of UMutex.
211  //
212  // Each UMutex has a corresponding Windows CRITICAL_SECTION.
213  // CRITICAL_SECTIONS must be initialized before use. This is done
214  //   with a InitOnceExcuteOnce operation.
215  //
216  // InitOnceExecuteOnce was introduced with Windows Vista. For now ICU
217  // must support Windows XP, so we roll our own. ICU will switch to the
218  // native Windows InitOnceExecuteOnce when possible.
219  
220  typedef UBool (*U_PINIT_ONCE_FN) (
221    U_INIT_ONCE     *initOnce,
222    void            *parameter,
223    void            **context
224  );
225  
u_InitOnceExecuteOnce(U_INIT_ONCE * initOnce,U_PINIT_ONCE_FN initFn,void * parameter,void ** context)226  UBool u_InitOnceExecuteOnce(
227    U_INIT_ONCE     *initOnce,
228    U_PINIT_ONCE_FN initFn,
229    void            *parameter,
230    void            **context) {
231        for (;;) {
232            long previousState = InterlockedCompareExchange(
233                &initOnce->fState,  //  Destination,
234                1,                  //  Exchange Value
235                0);                 //  Compare value
236            if (previousState == 2) {
237                // Initialization was already completed.
238                if (context != NULL) {
239                    *context = initOnce->fContext;
240                }
241                return TRUE;
242            }
243            if (previousState == 1) {
244                // Initialization is in progress in some other thread.
245                // Loop until it completes.
246                Sleep(1);
247                continue;
248            }
249  
250            // Initialization needed. Execute the callback function to do it.
251            U_ASSERT(previousState == 0);
252            U_ASSERT(initOnce->fState == 1);
253            UBool success = (*initFn)(initOnce, parameter, &initOnce->fContext);
254            U_ASSERT(success); // ICU is not supporting the failure case.
255  
256            // Assign the state indicating that initialization has completed.
257            // Using InterlockedCompareExchange to do it ensures that all
258            // threads will have a consistent view of memory.
259            previousState = InterlockedCompareExchange(&initOnce->fState, 2, 1);
260            U_ASSERT(previousState == 1);
261            // Next loop iteration will see the initialization and return.
262        }
263  };
264  
winMutexInit(U_INIT_ONCE * initOnce,void * param,void ** context)265  static UBool winMutexInit(U_INIT_ONCE *initOnce, void *param, void **context) {
266      UMutex *mutex = static_cast<UMutex *>(param);
267      U_ASSERT(sizeof(CRITICAL_SECTION) <= sizeof(mutex->fCS));
268      InitializeCriticalSection((CRITICAL_SECTION *)mutex->fCS);
269      return TRUE;
270  }
271  
272  /*
273   *   umtx_lock
274   */
275  U_CAPI void  U_EXPORT2
umtx_lock(UMutex * mutex)276  umtx_lock(UMutex *mutex) {
277      if (mutex == NULL) {
278          mutex = &globalMutex;
279      }
280      if (pMutexLockFn) {
281          usrMutexLock(mutex);
282      } else {
283          u_InitOnceExecuteOnce(&mutex->fInitOnce, winMutexInit, mutex, NULL);
284          EnterCriticalSection((CRITICAL_SECTION *)mutex->fCS);
285      }
286  }
287  
288  U_CAPI void  U_EXPORT2
umtx_unlock(UMutex * mutex)289  umtx_unlock(UMutex* mutex)
290  {
291      if (mutex == NULL) {
292          mutex = &globalMutex;
293      }
294      if (pMutexUnlockFn) {
295          (*pMutexUnlockFn)(gMutexContext, &mutex->fUserMutex);
296      } else {
297          LeaveCriticalSection((CRITICAL_SECTION *)mutex->fCS);
298      }
299  }
300  
301  #endif  // Windows Implementation
302  
303  
304  U_CAPI void U_EXPORT2
u_setMutexFunctions(const void * context,UMtxInitFn * i,UMtxFn * d,UMtxFn * l,UMtxFn * u,UErrorCode * status)305  u_setMutexFunctions(const void *context, UMtxInitFn *i, UMtxFn *d, UMtxFn *l, UMtxFn *u,
306                      UErrorCode *status) {
307      if (U_FAILURE(*status)) {
308          return;
309      }
310  
311      /* Can not set a mutex function to a NULL value  */
312      if (i==NULL || d==NULL || l==NULL || u==NULL) {
313          *status = U_ILLEGAL_ARGUMENT_ERROR;
314          return;
315      }
316  
317      /* If ICU is not in an initial state, disallow this operation. */
318      if (cmemory_inUse()) {
319          *status = U_INVALID_STATE_ERROR;
320          return;
321      }
322  
323      // Clean up any previously set user mutex functions.
324      // It's possible to call u_setMutexFunctions() more than once without without explicitly cleaning up,
325      // and the last call should take. Kind of a corner case, but it worked once, there is a test for
326      // it, so we keep it working. The global and impl mutexes will have been created by the
327      // previous u_setMutexFunctions(), and now need to be destroyed.
328  
329      usrMutexCleanup();
330  
331      /* Swap in the mutex function pointers.  */
332      pMutexInitFn    = i;
333      pMutexDestroyFn = d;
334      pMutexLockFn    = l;
335      pMutexUnlockFn  = u;
336      gMutexContext   = context;
337      gMutexListSize  = 0;
338  
339      /* Initialize the global and impl mutexes. Safe to do at this point because
340       * u_setMutexFunctions must be done in a single-threaded envioronment. Not thread safe.
341       */
342      (*pMutexInitFn)(gMutexContext, &globalMutex.fUserMutex, status);
343      globalMutex.fInitialized = TRUE;
344      (*pMutexInitFn)(gMutexContext, &implMutex.fUserMutex, status);
345      implMutex.fInitialized = TRUE;
346  }
347  
348  
349  
350  /*   synchronized compare and swap function, for use when OS or compiler built-in
351   *   equivalents aren't available.
352   */
mutexed_compare_and_swap(void ** dest,void * newval,void * oldval)353  static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval) {
354      umtx_lock(&implMutex);
355      void *temp = *dest;
356      if (temp == oldval) {
357          *dest = newval;
358      }
359      umtx_unlock(&implMutex);
360  
361      return temp;
362  }
363  
364  
365  
366  /*-----------------------------------------------------------------
367   *
368   *  Atomic Increment and Decrement
369   *     umtx_atomic_inc
370   *     umtx_atomic_dec
371   *
372   *----------------------------------------------------------------*/
373  
374  /* Pointers to user-supplied inc/dec functions.  Null if no funcs have been set.  */
375  static UMtxAtomicFn  *pIncFn = NULL;
376  static UMtxAtomicFn  *pDecFn = NULL;
377  static const void *gIncDecContext  = NULL;
378  
379  #if defined (POSIX) && (U_HAVE_GCC_ATOMICS == 0)
380  static UMutex   gIncDecMutex = U_MUTEX_INITIALIZER;
381  #endif
382  
383  U_CAPI int32_t U_EXPORT2
umtx_atomic_inc(int32_t * p)384  umtx_atomic_inc(int32_t *p)  {
385      int32_t retVal;
386      if (pIncFn) {
387          retVal = (*pIncFn)(gIncDecContext, p);
388      } else {
389          #if U_PLATFORM_HAS_WIN32_API
390              retVal = InterlockedIncrement((LONG*)p);
391          #elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
392              retVal = OSAtomicIncrement32Barrier(p);
393          #elif (U_HAVE_GCC_ATOMICS == 1)
394              retVal = __sync_add_and_fetch(p, 1);
395          #elif defined (POSIX)
396              umtx_lock(&gIncDecMutex);
397              retVal = ++(*p);
398              umtx_unlock(&gIncDecMutex);
399          #else
400              /* Unknown Platform. */
401              retVal = ++(*p);
402          #endif
403      }
404      return retVal;
405  }
406  
407  U_CAPI int32_t U_EXPORT2
umtx_atomic_dec(int32_t * p)408  umtx_atomic_dec(int32_t *p) {
409      int32_t retVal;
410      if (pDecFn) {
411          retVal = (*pDecFn)(gIncDecContext, p);
412      } else {
413          #if U_PLATFORM_HAS_WIN32_API
414              retVal = InterlockedDecrement((LONG*)p);
415          #elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
416              retVal = OSAtomicDecrement32Barrier(p);
417          #elif (U_HAVE_GCC_ATOMICS == 1)
418              retVal = __sync_sub_and_fetch(p, 1);
419          #elif defined (POSIX)
420              umtx_lock(&gIncDecMutex);
421              retVal = --(*p);
422              umtx_unlock(&gIncDecMutex);
423          #else
424              /* Unknown Platform. */
425              retVal = --(*p);
426          #endif
427      }
428      return retVal;
429  }
430  
431  
432  
433  U_CAPI void U_EXPORT2
u_setAtomicIncDecFunctions(const void * context,UMtxAtomicFn * ip,UMtxAtomicFn * dp,UErrorCode * status)434  u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp,
435                                  UErrorCode *status) {
436      if (U_FAILURE(*status)) {
437          return;
438      }
439      /* Can not set a mutex function to a NULL value  */
440      if (ip==NULL || dp==NULL) {
441          *status = U_ILLEGAL_ARGUMENT_ERROR;
442          return;
443      }
444      /* If ICU is not in an initial state, disallow this operation. */
445      if (cmemory_inUse()) {
446          *status = U_INVALID_STATE_ERROR;
447          return;
448      }
449  
450      pIncFn = ip;
451      pDecFn = dp;
452      gIncDecContext = context;
453  
454  #if U_DEBUG
455      {
456          int32_t   testInt = 0;
457          U_ASSERT(umtx_atomic_inc(&testInt) == 1);     /* Sanity Check.    Do the functions work at all? */
458          U_ASSERT(testInt == 1);
459          U_ASSERT(umtx_atomic_dec(&testInt) == 0);
460          U_ASSERT(testInt == 0);
461      }
462  #endif
463  }
464  
465  
466  /*
467   *  Mutex Cleanup Function
468   *      Reset the mutex function callback pointers.
469   *      Called from the global ICU u_cleanup() function.
470   */
umtx_cleanup(void)471  U_CFUNC UBool umtx_cleanup(void) {
472      /* Extra, do-nothing function call to suppress compiler warnings on platforms where
473       *   mutexed_compare_and_swap is not otherwise used.  */
474      void *pv = &globalMutex;
475      mutexed_compare_and_swap(&pv, NULL, NULL);
476      usrMutexCleanup();
477  
478      pIncFn          = NULL;
479      pDecFn          = NULL;
480      gIncDecContext  = NULL;
481  
482      return TRUE;
483  }
484