gc_locks.h raw

   1  /*
   2   * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
   3   * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
   4   * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
   5   * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved.
   6   * Copyright (c) 2008-2022 Ivan Maidanski
   7   *
   8   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
   9   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
  10   *
  11   * Permission is hereby granted to use or copy this program
  12   * for any purpose, provided the above notices are retained on all copies.
  13   * Permission to modify the code and to distribute modified code is granted,
  14   * provided the above notices are retained, and a notice that the code was
  15   * modified is included with the above copyright notice.
  16   */
  17  
  18  #ifndef GC_LOCKS_H
  19  #define GC_LOCKS_H
  20  
  21  #if !defined(GC_PRIVATE_H) && !defined(CPPCHECK)
  22  #  error gc_locks.h should be included from gc_priv.h
  23  #endif
  24  
  25  /*
  26   * Mutual exclusion between allocator/collector routines.  Needed if
  27   * there is more than one allocator thread.  Note that `I_HOLD_LOCK`,
  28   * `I_DONT_HOLD_LOCK` and `I_HOLD_READER_LOCK` macros are used only
  29   * positively in assertions, and may return `TRUE` in the "do not know"
  30   * case.
  31   */
  32  
  33  #ifdef THREADS
  34  
  35  EXTERN_C_BEGIN
  36  
  37  #  if defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH)
  38  extern void GC_lock(void);
  39  extern void GC_unlock(void);
  40  #    define UNCOND_LOCK() GC_lock()
  41  #    define UNCOND_UNLOCK() GC_unlock()
  42  #    ifdef GC_ASSERTIONS
  43  #      define SET_LOCK_HOLDER() (void)0
  44  #    endif
  45  #  endif
  46  
  47  #  if (!defined(AO_HAVE_test_and_set_acquire) || defined(GC_WIN32_THREADS) \
  48         || defined(LINT2) || defined(RTEMS) || defined(SN_TARGET_PS3)       \
  49         || defined(BASE_ATOMIC_OPS_EMULATED) || defined(USE_RWLOCK))        \
  50        && defined(GC_PTHREADS)
  51  #    define USE_PTHREAD_LOCKS
  52  #    undef USE_SPIN_LOCK
  53  #    if (defined(GC_WIN32_THREADS) || defined(LINT2) || defined(USE_RWLOCK)) \
  54          && !defined(NO_PTHREAD_TRYLOCK)
  55  /*
  56   * `pthread_mutex_trylock` may not win in `GC_lock` on Win32, due to
  57   * built-in support for spinning first?
  58   */
  59  #      define NO_PTHREAD_TRYLOCK
  60  #    endif
  61  #  endif
  62  
  63  #  if defined(GC_WIN32_THREADS) && !defined(USE_PTHREAD_LOCKS) \
  64        || defined(GC_PTHREADS)
  65  /* A value which is not equal to `NUMERIC_THREAD_ID(id)` for any thread. */
  66  #    define NO_THREAD ((unsigned long)(-1L))
  67  #    ifdef GC_ASSERTIONS
  68  GC_EXTERN unsigned long GC_lock_holder;
  69  #      define UNSET_LOCK_HOLDER() (void)(GC_lock_holder = NO_THREAD)
  70  #    endif
  71  #  endif /* GC_WIN32_THREADS || GC_PTHREADS */
  72  
  73  #  if defined(GC_WIN32_THREADS) && !defined(USE_PTHREAD_LOCKS)
  74  #    ifdef USE_RWLOCK
  75  GC_EXTERN SRWLOCK GC_allocate_ml;
  76  #    else
  77  GC_EXTERN CRITICAL_SECTION GC_allocate_ml;
  78  #    endif
  79  #    ifdef GC_ASSERTIONS
  80  #      define SET_LOCK_HOLDER() (void)(GC_lock_holder = GetCurrentThreadId())
  81  #      define I_HOLD_LOCK() \
  82          (!GC_need_to_lock || GC_lock_holder == GetCurrentThreadId())
  83  #      ifdef THREAD_SANITIZER
  84  #        define I_DONT_HOLD_LOCK() TRUE /*< conservatively say yes */
  85  #      else
  86  #        define I_DONT_HOLD_LOCK() \
  87            (!GC_need_to_lock || GC_lock_holder != GetCurrentThreadId())
  88  #      endif
  89  #      ifdef USE_RWLOCK
  90  #        define UNCOND_READER_LOCK()               \
  91            {                                        \
  92              GC_ASSERT(I_DONT_HOLD_LOCK());         \
  93              AcquireSRWLockShared(&GC_allocate_ml); \
  94            }
  95  #        define UNCOND_READER_UNLOCK()             \
  96            {                                        \
  97              GC_ASSERT(I_DONT_HOLD_LOCK());         \
  98              ReleaseSRWLockShared(&GC_allocate_ml); \
  99            }
 100  #        define UNCOND_LOCK()                         \
 101            {                                           \
 102              GC_ASSERT(I_DONT_HOLD_LOCK());            \
 103              AcquireSRWLockExclusive(&GC_allocate_ml); \
 104              SET_LOCK_HOLDER();                        \
 105            }
 106  #        define UNCOND_UNLOCK()                       \
 107            {                                           \
 108              GC_ASSERT(I_HOLD_LOCK());                 \
 109              UNSET_LOCK_HOLDER();                      \
 110              ReleaseSRWLockExclusive(&GC_allocate_ml); \
 111            }
 112  #      else
 113  #        define UNCOND_LOCK()                      \
 114            {                                        \
 115              GC_ASSERT(I_DONT_HOLD_LOCK());         \
 116              EnterCriticalSection(&GC_allocate_ml); \
 117              SET_LOCK_HOLDER();                     \
 118            }
 119  #        define UNCOND_UNLOCK()                    \
 120            {                                        \
 121              GC_ASSERT(I_HOLD_LOCK());              \
 122              UNSET_LOCK_HOLDER();                   \
 123              LeaveCriticalSection(&GC_allocate_ml); \
 124            }
 125  #      endif
 126  #    else
 127  #      ifdef USE_RWLOCK
 128  #        define UNCOND_READER_LOCK() AcquireSRWLockShared(&GC_allocate_ml)
 129  #        define UNCOND_READER_UNLOCK() ReleaseSRWLockShared(&GC_allocate_ml)
 130  #        define UNCOND_LOCK() AcquireSRWLockExclusive(&GC_allocate_ml)
 131  #        define UNCOND_UNLOCK() ReleaseSRWLockExclusive(&GC_allocate_ml)
 132  #      else
 133  #        define UNCOND_LOCK() EnterCriticalSection(&GC_allocate_ml)
 134  #        define UNCOND_UNLOCK() LeaveCriticalSection(&GC_allocate_ml)
 135  #      endif
 136  #    endif /* !GC_ASSERTIONS */
 137  #  elif defined(GC_PTHREADS)
 138  EXTERN_C_END
 139  #    include <pthread.h>
 140  EXTERN_C_BEGIN
 141  /*
 142   * POSIX allows `pthread_t` to be a structure type, though it rarely is.
 143   * Unfortunately, we need to use a `pthread_t` to index a data structure.
 144   * It also helps if comparisons do not involve a function call.
 145   * Hence we introduce platform-dependent macros to compare `pthread_t` ids
 146   * and to map them to integers (of `unsigned long` type).  This mapping
 147   * does not need to result in different values for each thread, though
 148   * that should be true as much as possible.
 149   */
 150  #    if !defined(GC_WIN32_PTHREADS)
 151  #      define NUMERIC_THREAD_ID(id) ((unsigned long)(GC_uintptr_t)(id))
 152  #      define THREAD_EQUAL(id1, id2) ((id1) == (id2))
 153  #      define NUMERIC_THREAD_ID_UNIQUE
 154  #    elif defined(__WINPTHREADS_VERSION_MAJOR) /*< winpthreads */
 155  #      define NUMERIC_THREAD_ID(id) ((unsigned long)(id))
 156  #      define THREAD_EQUAL(id1, id2) ((id1) == (id2))
 157  /* `NUMERIC_THREAD_ID()` is 32-bit and, thus, not unique on Win64. */
 158  #      ifndef _WIN64
 159  #        define NUMERIC_THREAD_ID_UNIQUE
 160  #      endif
 161  #    else /* pthreads-win32 */
 162  #      define NUMERIC_THREAD_ID(id) ((unsigned long)(word)(id.p))
 163  /*
 164   * The platform on which `pthread_t` is a structure.
 165   * Using documented internal details of pthreads-win32 library.
 166   * Faster than `pthread_equal()`.  Should not change with the
 167   * future versions of pthreads-win32 library.
 168   */
 169  #      define THREAD_EQUAL(id1, id2) (id1.p == id2.p && id1.x == id2.x)
 170  /*
 171   * Generic definitions based on `pthread_equal()` always work but will
 172   * result in poor performance (as `NUMERIC_THREAD_ID()` might give
 173   * a constant value) and weak assertion checking.
 174   */
 175  #      undef NUMERIC_THREAD_ID_UNIQUE
 176  #    endif
 177  
 178  #    ifdef SN_TARGET_PSP2
 179  EXTERN_C_END
 180  #      include "psp2-support.h"
 181  EXTERN_C_BEGIN
 182  GC_EXTERN WapiMutex GC_allocate_ml_PSP2;
 183  #      define UNCOND_LOCK()                           \
 184          {                                             \
 185            int res;                                    \
 186            GC_ASSERT(I_DONT_HOLD_LOCK());              \
 187            res = PSP2_MutexLock(&GC_allocate_ml_PSP2); \
 188            GC_ASSERT(0 == res);                        \
 189            (void)res;                                  \
 190            SET_LOCK_HOLDER();                          \
 191          }
 192  #      define UNCOND_UNLOCK()                           \
 193          {                                               \
 194            int res;                                      \
 195            GC_ASSERT(I_HOLD_LOCK());                     \
 196            UNSET_LOCK_HOLDER();                          \
 197            res = PSP2_MutexUnlock(&GC_allocate_ml_PSP2); \
 198            GC_ASSERT(0 == res);                          \
 199            (void)res;                                    \
 200          }
 201  
 202  #    elif (!defined(THREAD_LOCAL_ALLOC) || defined(USE_SPIN_LOCK))   \
 203          && !defined(USE_PTHREAD_LOCKS) && !defined(THREAD_SANITIZER) \
 204          && !defined(USE_RWLOCK)
 205  /*
 206   * In the `THREAD_LOCAL_ALLOC` case, the allocator lock tends to
 207   * be held for long periods, if it is held at all.
 208   * Thus spinning and sleeping for fixed periods are likely to result
 209   * in significant wasted time.  We thus rely mostly on queued locks.
 210   */
 211  #      undef USE_SPIN_LOCK
 212  #      define USE_SPIN_LOCK
 213  GC_INNER void GC_lock(void);
 214  #      ifdef GC_ASSERTIONS
 215  #        define UNCOND_LOCK()                                            \
 216            {                                                              \
 217              GC_ASSERT(I_DONT_HOLD_LOCK());                               \
 218              if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \
 219                GC_lock();                                                 \
 220              SET_LOCK_HOLDER();                                           \
 221            }
 222  #        define UNCOND_UNLOCK()          \
 223            {                              \
 224              GC_ASSERT(I_HOLD_LOCK());    \
 225              UNSET_LOCK_HOLDER();         \
 226              AO_CLEAR(&GC_allocate_lock); \
 227            }
 228  #      else
 229  #        define UNCOND_LOCK()                                            \
 230            {                                                              \
 231              if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \
 232                GC_lock();                                                 \
 233            }
 234  #        define UNCOND_UNLOCK() AO_CLEAR(&GC_allocate_lock)
 235  #      endif /* !GC_ASSERTIONS */
 236  #    else
 237  #      ifndef USE_PTHREAD_LOCKS
 238  #        define USE_PTHREAD_LOCKS
 239  #      endif
 240  #    endif /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */
 241  #    ifdef USE_PTHREAD_LOCKS
 242  EXTERN_C_END
 243  #      include <pthread.h>
 244  EXTERN_C_BEGIN
 245  #      ifdef GC_ASSERTIONS
 246  GC_INNER void GC_lock(void);
 247  #        define UNCOND_LOCK()              \
 248            {                                \
 249              GC_ASSERT(I_DONT_HOLD_LOCK()); \
 250              GC_lock();                     \
 251              SET_LOCK_HOLDER();             \
 252            }
 253  #      endif
 254  #      ifdef USE_RWLOCK
 255  GC_EXTERN pthread_rwlock_t GC_allocate_ml;
 256  #        ifdef GC_ASSERTIONS
 257  #          define UNCOND_READER_LOCK()                      \
 258              {                                               \
 259                GC_ASSERT(I_DONT_HOLD_LOCK());                \
 260                (void)pthread_rwlock_rdlock(&GC_allocate_ml); \
 261              }
 262  #          define UNCOND_READER_UNLOCK()                    \
 263              {                                               \
 264                GC_ASSERT(I_DONT_HOLD_LOCK());                \
 265                (void)pthread_rwlock_unlock(&GC_allocate_ml); \
 266              }
 267  #          define UNCOND_UNLOCK()                           \
 268              {                                               \
 269                GC_ASSERT(I_HOLD_LOCK());                     \
 270                UNSET_LOCK_HOLDER();                          \
 271                (void)pthread_rwlock_unlock(&GC_allocate_ml); \
 272              }
 273  #        else
 274  #          define UNCOND_READER_LOCK() \
 275              (void)pthread_rwlock_rdlock(&GC_allocate_ml)
 276  #          define UNCOND_READER_UNLOCK() UNCOND_UNLOCK()
 277  #          define UNCOND_LOCK() (void)pthread_rwlock_wrlock(&GC_allocate_ml)
 278  #          define UNCOND_UNLOCK() (void)pthread_rwlock_unlock(&GC_allocate_ml)
 279  #        endif /* !GC_ASSERTIONS */
 280  #      else
 281  GC_EXTERN pthread_mutex_t GC_allocate_ml;
 282  #        ifdef GC_ASSERTIONS
 283  #          define UNCOND_UNLOCK()                    \
 284              {                                        \
 285                GC_ASSERT(I_HOLD_LOCK());              \
 286                UNSET_LOCK_HOLDER();                   \
 287                pthread_mutex_unlock(&GC_allocate_ml); \
 288              }
 289  #        else
 290  #          if defined(NO_PTHREAD_TRYLOCK)
 291  #            define UNCOND_LOCK() pthread_mutex_lock(&GC_allocate_ml)
 292  #          else
 293  GC_INNER void GC_lock(void);
 294  #            define UNCOND_LOCK()                                \
 295                {                                                  \
 296                  if (pthread_mutex_trylock(&GC_allocate_ml) != 0) \
 297                    GC_lock();                                     \
 298                }
 299  #          endif
 300  #          define UNCOND_UNLOCK() pthread_mutex_unlock(&GC_allocate_ml)
 301  #        endif /* !GC_ASSERTIONS */
 302  #      endif
 303  #    endif /* USE_PTHREAD_LOCKS */
 304  #    ifdef GC_ASSERTIONS
 305  /* The allocator lock holder. */
 306  #      define SET_LOCK_HOLDER() \
 307          (void)(GC_lock_holder = NUMERIC_THREAD_ID(pthread_self()))
 308  #      define I_HOLD_LOCK() \
 309          (!GC_need_to_lock   \
 310           || GC_lock_holder == NUMERIC_THREAD_ID(pthread_self()))
 311  #      if !defined(NUMERIC_THREAD_ID_UNIQUE) || defined(THREAD_SANITIZER)
 312  #        define I_DONT_HOLD_LOCK() TRUE /*< conservatively say yes */
 313  #      else
 314  #        define I_DONT_HOLD_LOCK() \
 315            (!GC_need_to_lock        \
 316             || GC_lock_holder != NUMERIC_THREAD_ID(pthread_self()))
 317  #      endif
 318  #    endif /* GC_ASSERTIONS */
 319  #    if !defined(GC_WIN32_THREADS)
 320  /*
 321   * A hint that we are in the collector and holding the allocator lock
 322   * for an extended period.
 323   */
 324  GC_EXTERN volatile unsigned char GC_collecting;
 325  
 326  #      ifdef AO_HAVE_char_store
 327  #        if defined(GC_ASSERTIONS) && defined(AO_HAVE_char_fetch_and_add1)
 328  /* Ensure `ENTER_GC()` is not used recursively. */
 329  #          define ENTER_GC() GC_ASSERT(!AO_char_fetch_and_add1(&GC_collecting))
 330  #        else
 331  #          define ENTER_GC() AO_char_store(&GC_collecting, TRUE)
 332  #        endif
 333  #        define EXIT_GC() AO_char_store(&GC_collecting, FALSE)
 334  #      else
 335  #        define ENTER_GC() (void)(GC_collecting = TRUE)
 336  #        define EXIT_GC() (void)(GC_collecting = FALSE)
 337  #      endif
 338  #    endif
 339  #  endif /* GC_PTHREADS */
 340  #  if defined(GC_ALWAYS_MULTITHREADED) \
 341        && (defined(USE_PTHREAD_LOCKS) || defined(USE_SPIN_LOCK))
 342  #    define GC_need_to_lock TRUE
 343  #    define set_need_to_lock() (void)0
 344  #  else
 345  #    if defined(GC_ALWAYS_MULTITHREADED) && !defined(CPPCHECK)
 346  #      error Runtime initialization of the allocator lock is needed!
 347  #    endif
 348  #    undef GC_ALWAYS_MULTITHREADED
 349  GC_EXTERN GC_bool GC_need_to_lock;
 350  #    ifdef THREAD_SANITIZER
 351  /*
 352   * To workaround TSan false positive (e.g., when `GC_pthread_create()` is
 353   * called from multiple threads in parallel), do not set `GC_need_to_lock`
 354   * if it is already set.
 355   */
 356  #      define set_need_to_lock()                     \
 357          (void)(*(GC_bool volatile *)&GC_need_to_lock \
 358                     ? FALSE                           \
 359                     : (GC_need_to_lock = TRUE))
 360  #    else
 361  #      define set_need_to_lock() (void)(GC_need_to_lock = TRUE)
 362  /* We are multi-threaded now. */
 363  #    endif
 364  #  endif
 365  
 366  EXTERN_C_END
 367  
 368  #else /* !THREADS */
 369  #  define LOCK() (void)0
 370  #  define UNLOCK() (void)0
 371  #  ifdef GC_ASSERTIONS
 372  /*
 373   * `I_HOLD_LOCK()` and `I_DONT_HOLD_LOCK()` are used only in positive
 374   * assertions or to test whether we still need to acquire the allocator
 375   * lock; `TRUE` works in either case.
 376   */
 377  #    define I_HOLD_LOCK() TRUE
 378  #    define I_DONT_HOLD_LOCK() TRUE
 379  #  endif
 380  #endif /* !THREADS */
 381  
 382  #if defined(UNCOND_LOCK) && !defined(LOCK)
 383  #  if (defined(LINT2) && defined(USE_PTHREAD_LOCKS)) \
 384        || defined(GC_ALWAYS_MULTITHREADED)
 385  /*
 386   * Instruct code analysis tools not to care about `GC_need_to_lock`
 387   * influence to `LOCK`/`UNLOCK` semantic.
 388   */
 389  #    define LOCK() UNCOND_LOCK()
 390  #    define UNLOCK() UNCOND_UNLOCK()
 391  #    ifdef UNCOND_READER_LOCK
 392  #      define READER_LOCK() UNCOND_READER_LOCK()
 393  #      define READER_UNLOCK() UNCOND_READER_UNLOCK()
 394  #    endif
 395  #  else
 396  /* At least two thread running; need to lock. */
 397  #    define LOCK()           \
 398        do {                   \
 399          if (GC_need_to_lock) \
 400            UNCOND_LOCK();     \
 401        } while (0)
 402  #    define UNLOCK()         \
 403        do {                   \
 404          if (GC_need_to_lock) \
 405            UNCOND_UNLOCK();   \
 406        } while (0)
 407  #    ifdef UNCOND_READER_LOCK
 408  #      define READER_LOCK()       \
 409          do {                      \
 410            if (GC_need_to_lock)    \
 411              UNCOND_READER_LOCK(); \
 412          } while (0)
 413  #      define READER_UNLOCK()       \
 414          do {                        \
 415            if (GC_need_to_lock)      \
 416              UNCOND_READER_UNLOCK(); \
 417          } while (0)
 418  #    endif
 419  #  endif
 420  #endif /* UNCOND_LOCK && !LOCK */
 421  
 422  #ifdef READER_LOCK
 423  #  define HAS_REAL_READER_LOCK
 424  /* TODO: Implement I_HOLD_READER_LOCK, conservatively say yes for now. */
 425  #  define I_HOLD_READER_LOCK() TRUE
 426  #else
 427  #  define READER_LOCK() LOCK()
 428  #  define READER_UNLOCK() UNLOCK()
 429  #  ifdef GC_ASSERTIONS
 430  /*
 431   * A macro to check that the allocator lock is held at least in the
 432   * reader mode.
 433   */
 434  #    define I_HOLD_READER_LOCK() I_HOLD_LOCK()
 435  #  endif
 436  #endif /* !READER_LOCK */
 437  
 438  /*
 439   * A variant of `READER_UNLOCK()` which ensures that data written
 440   * before the unlock will be visible to the thread which acquires the
 441   * allocator lock in the exclusive mode.  But according to some `rwlock`
 442   * documentation: writers synchronize with prior writers and readers.
 443   */
 444  #define READER_UNLOCK_RELEASE() READER_UNLOCK()
 445  
 446  #ifndef ENTER_GC
 447  #  define ENTER_GC()
 448  #  define EXIT_GC()
 449  #endif
 450  
 451  #endif /* GC_LOCKS_H */
 452