pthread_support.h raw

   1  /*
   2   * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
   3   * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
   4   * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
   5   * Copyright (c) 2000-2009 by Hewlett-Packard Development Company.
   6   * All rights reserved.
   7   * Copyright (c) 2009-2022 Ivan Maidanski
   8   *
   9   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
  10   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
  11   *
  12   * Permission is hereby granted to use or copy this program
  13   * for any purpose, provided the above notices are retained on all copies.
  14   * Permission to modify the code and to distribute modified code is granted,
  15   * provided the above notices are retained, and a notice that the code was
  16   * modified is included with the above copyright notice.
  17   */
  18  
  19  /* Private declarations for thread support. */
  20  
  21  #ifndef GC_PTHREAD_SUPPORT_H
  22  #define GC_PTHREAD_SUPPORT_H
  23  
  24  #include "gc_priv.h"
  25  
  26  #ifdef THREADS
  27  
  28  #  if defined(GC_PTHREADS) || defined(GC_PTHREADS_PARAMARK)
  29  #    include <pthread.h>
  30  #  endif
  31  
  32  #  ifdef DARWIN
  33  #    include <mach/mach.h>
  34  #    include <mach/thread_act.h>
  35  #  endif
  36  
  37  #  ifdef THREAD_LOCAL_ALLOC
  38  #    include "thread_local_alloc.h"
  39  #  endif
  40  
  41  #  ifdef THREAD_SANITIZER
  42  #    include "dbg_mlc.h" /*< for `oh` type */
  43  #  endif
  44  
  45  EXTERN_C_BEGIN
  46  
  47  #  ifdef WOW64_THREAD_CONTEXT_WORKAROUND
  48  #    if defined(__WATCOMC__) && defined(GC_WIN32_THREADS)
  49  /*
  50   * Open Watcom (as of v2 2024-11-02 Build) does not define `NT_TIB`
  51   * in the platform `winnt.h` file.
  52   */
  53  struct GC_NT_TIB_s {
  54    /* `EXCEPTION_REGISTRATION_RECORD *` */ void *ExceptionList; /*< unused */
  55    PVOID StackBase;
  56    PVOID StackLimit;
  57    /* The remaining fields are unused. */
  58  };
  59  typedef struct GC_NT_TIB_s GC_NT_TIB;
  60  #    else
  61  #      define GC_NT_TIB NT_TIB
  62  #    endif
  63  #  endif
  64  
  65  struct GC_StackContext_Rep {
  66  #  if defined(THREAD_SANITIZER) && defined(SIGNAL_BASED_STOP_WORLD)
  67    /*
  68     * A dummy field to avoid TSan false positive about the race
  69     * between `GC_has_other_debug_info` and `GC_suspend_handler_inner`
  70     * (the latter one sets `stack_ptr`).
  71     */
  72    char dummy[sizeof(oh)];
  73  #  endif
  74  
  75    /*
  76     * Cold end of the stack (except for main thread on non-Windows).
  77     * On Windows: 0 means entry invalid; not `in_use` implies `stack_end`
  78     * is zero.
  79     */
  80  #  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
  81    volatile
  82  #  endif
  83        ptr_t stack_end;
  84  
  85    /* Valid only in some platform-specific states. */
  86    ptr_t stack_ptr;
  87  
  88  #  ifdef GC_WIN32_THREADS
  89  #    define ADDR_LIMIT ((ptr_t)GC_WORD_MAX)
  90    /*
  91     * Last known minimum (hottest) address in stack or `ADDR_LIMIT`
  92     * if unset.
  93     */
  94    ptr_t last_stack_min;
  95  #    ifdef I386
  96    /*
  97     * The cold end of the stack saved by `GC_record_stack_base` (never
  98     * modified by `GC_set_stackbottom()`); used for the old way of the
  99     * coroutines support.
 100     */
 101    ptr_t initial_stack_base;
 102  #    endif
 103  #  elif defined(DARWIN) && !defined(DARWIN_DONT_PARSE_STACK)
 104    /*
 105     * Result of `GC_FindTopOfStack(0)`; valid only if the thread is blocked;
 106     * non-`NULL` value means already set.
 107     */
 108    ptr_t topOfStack;
 109  #  endif
 110  
 111  #  if defined(E2K) || defined(IA64)
 112    ptr_t backing_store_end;
 113    ptr_t backing_store_ptr;
 114  #  endif
 115  
 116  #  ifdef GC_WIN32_THREADS
 117    /* For now, alt-stack is not implemented for Win32. */
 118  #  else
 119    /* The start of the alt-stack if there is one, `NULL` otherwise. */
 120    ptr_t altstack;
 121  
 122    /* Same for the "normal" stack (set by `GC_register_altstack`). */
 123    ptr_t normstack;
 124  
 125    /* The size of the alt-stack if exists. */
 126    size_t altstack_size;
 127  
 128    size_t normstack_size;
 129  #  endif
 130  
 131  #  ifdef E2K
 132    /* The current offset in the procedure stack. */
 133    size_t ps_ofs;
 134  #  endif
 135  
 136  #  ifndef GC_NO_FINALIZATION
 137    unsigned char finalizer_nested;
 138  
 139    /*
 140     * Used by `GC_check_finalizer_nested()` to minimize the level of recursion
 141     * when a client finalizer allocates memory.  Initially it is zero and
 142     * `finalizer_nested` is zero.
 143     */
 144    unsigned short finalizer_skipped;
 145  #  endif
 146  
 147    /*
 148     * Points to the "frame" data held in stack by the innermost
 149     * `GC_call_with_gc_active()` of this stack (thread); may be `NULL`.
 150     */
 151    struct GC_traced_stack_sect_s *traced_stack_sect;
 152  };
 153  typedef struct GC_StackContext_Rep *GC_stack_context_t;
 154  
 155  #  ifdef GC_WIN32_THREADS
 156  typedef DWORD thread_id_t;
 157  #    define thread_id_self() GetCurrentThreadId()
 158  #    define THREAD_ID_EQUAL(id1, id2) ((id1) == (id2))
 159  /*
 160   * Convert a platform-specific thread `id` (of `thread_id_t` type) to some
 161   * pointer identifying the thread.  This is used mostly for a debugging
 162   * purpose when printing thread id.
 163   */
 164  #    define THREAD_ID_TO_VPTR(id) CAST_THRU_UINTPTR(void *, id)
 165  #  else
 166  typedef pthread_t thread_id_t;
 167  #    define thread_id_self() pthread_self()
 168  #    define THREAD_ID_EQUAL(id1, id2) THREAD_EQUAL(id1, id2)
 169  #    define THREAD_ID_TO_VPTR(id) PTHREAD_TO_VPTR(id)
 170  #  endif
 171  
 172  struct GC_Thread_Rep {
 173    union {
 174  #  if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS)
 175      /*
 176       * Updated without a lock.  We assert that each unused entry has
 177       * invalid `id` of zero and zero `stack_end`.
 178       * Used only if `GC_win32_dll_threads`.
 179       */
 180      volatile AO_t in_use;
 181  
 182      /*
 183       * The same but of the type that matches the first argument of
 184       * `InterlockedExchange()`; `volatile` is omitted because the
 185       * ancient version of the function prototype lacked the qualifier.
 186       */
 187      LONG long_in_use;
 188  #  endif
 189  
 190      /*
 191       * Hash table link without `GC_win32_dll_threads`.  More recently
 192       * allocated threads with a given `pthreads` id come first.
 193       * (All but the first are guaranteed to be dead, but we may not yet
 194       * have registered the join.)
 195       */
 196      struct GC_Thread_Rep *next;
 197    } tm; /*< table_management */
 198  
 199    GC_stack_context_t crtn;
 200  
 201    thread_id_t id; /*< hash table key */
 202  #  ifdef DARWIN
 203    mach_port_t mach_thread;
 204  #  elif defined(GC_WIN32_THREADS) && defined(GC_PTHREADS)
 205    pthread_t pthread_id;
 206  #  elif defined(USE_TKILL_ON_ANDROID)
 207    pid_t kernel_id;
 208  #  endif
 209  
 210  #  ifdef MSWINCE
 211    /*
 212     * According to MSDN docs for WinCE targets:
 213     *   - `DuplicateHandle()` is not applicable to thread handles;
 214     *   - the value returned by `GetCurrentThreadId()` could be used as
 215     *     a "real" thread handle (for `SuspendThread()`, `ResumeThread()`
 216     *     and `GetThreadContext()`).
 217     */
 218  #    define THREAD_HANDLE(p) ((HANDLE)(word)(p)->id)
 219  #  elif defined(GC_WIN32_THREADS)
 220    HANDLE handle;
 221  #    define THREAD_HANDLE(p) ((p)->handle)
 222  #  endif /* GC_WIN32_THREADS && !MSWINCE */
 223  
 224    /* Note: this is protected by the allocator lock. */
 225    unsigned char flags;
 226  
 227    /* Thread has exited (`pthreads` only). */
 228  #  define FINISHED 0x1
 229  #  ifndef GC_PTHREADS
 230  #    define KNOWN_FINISHED(p) FALSE
 231  #  else
 232  #    define KNOWN_FINISHED(p) (((p)->flags & FINISHED) != 0)
 233    /*
 234     * Thread is treated as detached.  Thread may really be detached,
 235     * or it may have been explicitly registered, in which case we can
 236     * deallocate its `GC_Thread_Rep` entity once it unregisters itself,
 237     * since it may not return a pointer to the collector heap.
 238     */
 239  #    define DETACHED 0x2
 240  #  endif
 241  #  if (defined(GC_HAVE_PTHREAD_EXIT) || !defined(GC_NO_PTHREAD_CANCEL)) \
 242        && defined(GC_PTHREADS)
 243    /* Collections are disabled while the thread is exiting. */
 244  #    define DISABLED_GC 0x10
 245  #  endif
 246    /*
 247     * Thread is in the do-blocking state.  If the flag is set, the thread
 248     * has stored its stack pointer value and will acquire the allocator lock
 249     * before any pointer manipulation.  Thus, it does not need a signal sent
 250     * to stop it.
 251     */
 252  #  define DO_BLOCKING 0x20
 253  #  ifdef GC_WIN32_THREADS
 254    /* Thread is suspended by `SuspendThread()`. */
 255  #    define IS_SUSPENDED 0x40
 256  #  endif
 257  
 258  #  ifdef SIGNAL_BASED_STOP_WORLD
 259    /*
 260     * The value of `GC_stop_count` when the thread last successfully
 261     * handled a suspend signal.
 262     */
 263    volatile AO_t last_stop_count;
 264  #    ifdef GC_ENABLE_SUSPEND_THREAD
 265    /*
 266     * Note: an odd value means thread was suspended externally;
 267     * incremented on every call of `GC_suspend_thread()` and
 268     * `GC_resume_thread()`; updated with the allocator lock held, but
 269     * could be read from a signal handler.
 270     */
 271    volatile AO_t ext_suspend_cnt;
 272  #    endif
 273  #  endif
 274  
 275  #  ifdef GC_PTHREADS
 276    /*
 277     * The value returned from the thread.  Used only to avoid premature
 278     * reclamation of any data it might reference.  This is unfortunately
 279     * also the reason we need to intercept join and detach.
 280     */
 281    void *status;
 282  #  endif
 283  
 284  #  ifdef THREAD_LOCAL_ALLOC
 285    struct thread_local_freelists tlfs GC_ATTR_PTRT_ALIGNED;
 286  #  endif
 287  
 288  #  ifdef NACL
 289    /*
 290     * Grab `NACL_GC_REG_STORAGE_SIZE` pointers off the stack when going
 291     * into a `syscall()`.  20 is more than we need, but it is
 292     * an overestimate in case the instrumented function uses any
 293     * callee-saved registers, they may be pushed to the stack much
 294     * earlier.  Also, on x86_64 `push` puts 8 bytes on the stack even
 295     * though our pointers have 4 bytes in length.
 296     */
 297  #    ifdef ARM32
 298    /* Space for `r4` ... `r8`, `r10`, ... `r12`, `r14`. */
 299  #      define NACL_GC_REG_STORAGE_SIZE 9
 300  #    else
 301  #      define NACL_GC_REG_STORAGE_SIZE 20
 302  #    endif
 303    ptr_t reg_storage[NACL_GC_REG_STORAGE_SIZE];
 304  #  elif defined(PLATFORM_HAVE_GC_REG_STORAGE_SIZE)
 305    word registers[PLATFORM_GC_REG_STORAGE_SIZE]; /*< used externally */
 306  #  endif
 307  
 308  #  if defined(WOW64_THREAD_CONTEXT_WORKAROUND) && defined(MSWINRT_FLAVOR)
 309    GC_NT_TIB *tib;
 310  #  endif
 311  
 312  #  ifdef RETRY_GET_THREAD_CONTEXT /* `&& GC_WIN32_THREADS` */
 313    ptr_t context_sp;
 314  
 315    /*
 316     * Populated as part of `GC_suspend()` as resume/suspend loop may be
 317     * needed for `GetThreadContext()` to succeed.
 318     */
 319    word context_regs[PUSHED_REGS_COUNT];
 320  #  endif
 321  };
 322  typedef struct GC_Thread_Rep *GC_thread;
 323  
 324  #  if defined(GC_PTHREADS) || defined(GC_PTHREADS_PARAMARK)
 325  /* Convert an opaque `pthread_t` value to a pointer identifying the thread. */
 326  #    if defined(GC_WIN32_THREADS) && !defined(CYGWIN32)                  \
 327          && (defined(GC_WIN32_PTHREADS) || defined(GC_PTHREADS_PARAMARK)) \
 328          && !defined(__WINPTHREADS_VERSION_MAJOR)
 329  /*
 330   * Using documented internal details of pthreads-win32 library.
 331   * `pthread_t` is a structure.
 332   */
 333  #      define PTHREAD_TO_VPTR(id) ((void *)(id).p)
 334  #    else
 335  #      define PTHREAD_TO_VPTR(id) CAST_THRU_UINTPTR(void *, id)
 336  #    endif
 337  #  endif
 338  
 339  #  ifndef THREAD_TABLE_SZ
 340  /* Note: this is a power of 2 (for speed). */
 341  #    define THREAD_TABLE_SZ 256
 342  #  endif
 343  
 344  #  ifdef GC_WIN32_THREADS
 345  #    define THREAD_TABLE_INDEX(id) /*< `id` is of `DWORD` type */ \
 346        (int)((((id) >> 8) ^ (id)) % THREAD_TABLE_SZ)
 347  #  elif CPP_WORDSZ > 32
 348  #    define THREAD_TABLE_INDEX(id)                                          \
 349        (int)(((((NUMERIC_THREAD_ID(id) >> 8) ^ NUMERIC_THREAD_ID(id)) >> 16) \
 350               ^ ((NUMERIC_THREAD_ID(id) >> 8) ^ NUMERIC_THREAD_ID(id)))      \
 351              % THREAD_TABLE_SZ)
 352  #  else
 353  #    define THREAD_TABLE_INDEX(id)                                        \
 354        (int)(((NUMERIC_THREAD_ID(id) >> 16) ^ (NUMERIC_THREAD_ID(id) >> 8) \
 355               ^ NUMERIC_THREAD_ID(id))                                     \
 356              % THREAD_TABLE_SZ)
 357  #  endif
 358  
 359  /*
 360   * The set (hash table) of all known threads.  We intercept thread
 361   * creation and join/detach.  Protected by the allocator lock.
 362   * Not used if `GC_win32_dll_threads`.
 363   */
 364  GC_EXTERN GC_thread GC_threads[THREAD_TABLE_SZ];
 365  
 366  #  ifndef MAX_MARKERS
 367  #    define MAX_MARKERS 16
 368  #  endif
 369  
 370  #  ifdef GC_ASSERTIONS
 371  GC_EXTERN GC_bool GC_thr_initialized;
 372  #  endif
 373  
 374  #  ifdef STACKPTR_CORRECTOR_AVAILABLE
 375  GC_EXTERN GC_sp_corrector_proc GC_sp_corrector;
 376  #  endif
 377  
 378  GC_EXTERN GC_on_thread_event_proc GC_on_thread_event;
 379  
 380  #  ifdef GC_WIN32_THREADS
 381  
 382  #    ifdef GC_NO_THREADS_DISCOVERY
 383  #      define GC_win32_dll_threads FALSE
 384  #    elif defined(GC_DISCOVER_TASK_THREADS)
 385  #      define GC_win32_dll_threads TRUE
 386  #    else
 387  /*
 388   * `GC_win32_dll_threads` must be set (if needed) at the application
 389   * initialization time, i.e. before any collector or thread calls.
 390   * We make it a "dynamic" option only to avoid multiple library versions.
 391   */
 392  GC_EXTERN GC_bool GC_win32_dll_threads;
 393  #    endif
 394  
 395  #    ifdef PARALLEL_MARK
 396  GC_EXTERN int GC_available_markers_m1;
 397  
 398  /*
 399   * The desired amount of marker threads (including the initiating one).
 400   * Note: the default value (0) means the number of markers should be
 401   * selected automatically.
 402   */
 403  GC_EXTERN unsigned GC_required_markers_cnt;
 404  
 405  /* The cold end of the stack for markers. */
 406  GC_EXTERN ptr_t GC_marker_sp[MAX_MARKERS - 1];
 407  
 408  /*
 409   * Last known minimum (hottest) address in stack (or `ADDR_LIMIT` if
 410   * unset) for markers.
 411   */
 412  GC_EXTERN ptr_t GC_marker_last_stack_min[MAX_MARKERS - 1];
 413  
 414  #      ifndef GC_PTHREADS_PARAMARK
 415  /*
 416   * A table mapping the helper thread id to the mark helper index
 417   * (linear search is used since the mapping contains only a few entries).
 418   */
 419  GC_EXTERN thread_id_t GC_marker_Id[MAX_MARKERS - 1];
 420  #      endif
 421  
 422  #      if !defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) && !defined(MSWINCE)
 423  GC_INNER void GC_init_win32_thread_naming(HMODULE hK32);
 424  #      endif
 425  #      ifdef GC_PTHREADS_PARAMARK
 426  GC_INNER void *GC_mark_thread(void *);
 427  #      elif defined(MSWINCE)
 428  GC_INNER DWORD WINAPI GC_mark_thread(LPVOID);
 429  #      else
 430  GC_INNER unsigned __stdcall GC_mark_thread(void *);
 431  #      endif
 432  #    endif /* PARALLEL_MARK */
 433  
 434  /*
 435   * Add a thread to `GC_threads`.  We assume it was not already there.
 436   * Note: the `id` field should be set by the caller.
 437   */
 438  GC_INNER GC_thread GC_new_thread(thread_id_t);
 439  
 440  GC_INNER void GC_record_stack_base(GC_stack_context_t crtn,
 441                                     const struct GC_stack_base *sb);
 442  
 443  /*
 444   * This may be called from `DllMain`, and hence operates under unusual
 445   * constraints.  In particular, it must be lock-free if
 446   * `GC_win32_dll_threads`.  Always called from the thread being added.
 447   * If not `GC_win32_dll_threads`, then we already hold the allocator
 448   * lock except possibly during single-threaded startup code.
 449   * Does not initialize thread-local free lists.
 450   */
 451  GC_INNER GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb,
 452                                                 thread_id_t self_id);
 453  
 454  #    ifdef GC_PTHREADS
 455  GC_INNER void GC_win32_cache_self_pthread(thread_id_t);
 456  #    else
 457  /*
 458   * Delete a thread from `GC_threads`.  We assume it is there.
 459   * (The code intentionally traps if it was not.)  It is also safe to delete
 460   * the main thread.  If `GC_win32_dll_threads`, it should be called only
 461   * from the thread being deleted (except for `DLL_PROCESS_DETACH` case).
 462   * If a thread has been joined, but we have not yet been notified, then
 463   * there may be more than one thread in the table with the same thread
 464   * id - this is OK because we delete a specific one.
 465   */
 466  GC_INNER void GC_delete_thread(GC_thread);
 467  #    endif
 468  
 469  #    ifdef CAN_HANDLE_FORK
 470  /* Prepare for a process fork if requested. */
 471  GC_INNER void GC_setup_atfork(void);
 472  #    endif
 473  
 474  #    if !defined(DONT_USE_ATEXIT) || !defined(GC_NO_THREADS_DISCOVERY)
 475  GC_EXTERN thread_id_t GC_main_thread_id;
 476  #    endif
 477  
 478  #    ifndef GC_NO_THREADS_DISCOVERY
 479  /*
 480   * Search in `dll_thread_table` and return the `GC_thread[]` entity
 481   * corresponding to the given thread `id`.
 482   * May be called without a lock, but should be called in contexts in
 483   * those the requested thread cannot be asynchronously deleted, e.g.
 484   * from the thread itself.
 485   */
 486  GC_INNER GC_thread GC_win32_dll_lookup_thread(thread_id_t id);
 487  #    endif
 488  
 489  #    ifdef MPROTECT_VDB
 490  /*
 491   * Make sure given thread descriptor is not protected by the VDB
 492   * implementation.  Used to prevent write faults when the world is
 493   * (partially) stopped, since it may have been stopped with a system
 494   * lock held, and that lock may be required for fault handling.
 495   */
 496  GC_INNER void GC_win32_unprotect_thread(GC_thread);
 497  #    else
 498  #      define GC_win32_unprotect_thread(t) (void)(t)
 499  #    endif /* !MPROTECT_VDB */
 500  
 501  #  else
 502  #    define GC_win32_dll_threads FALSE
 503  #  endif /* !GC_WIN32_THREADS */
 504  
 505  #  ifdef GC_PTHREADS
 506  #    ifdef GC_WIN32_THREADS
 507  /*
 508   * Return a `GC_thread` corresponding to a given `pthread_t`, or
 509   * `NULL` if it is not there.  We assume that this is only called for
 510   * `pthreads` ids that have not yet terminated or are still joinable,
 511   * and cannot be terminated concurrently.
 512   */
 513  GC_INNER GC_thread GC_lookup_by_pthread(pthread_t);
 514  #    else
 515  #      define GC_lookup_by_pthread(t) GC_lookup_thread(t)
 516  #    endif
 517  #  endif /* GC_PTHREADS */
 518  
 519  /*
 520   * Return a `GC_thread` corresponding to a given thread `id`, or
 521   * `NULL` if it is not there.  Caller holds the allocator lock in the
 522   * reader mode, at least, or otherwise inhibits updates.  If there is more
 523   * than one thread with the given `id`, we return the most recent one.
 524   */
 525  GC_INNER GC_thread GC_lookup_thread(thread_id_t id);
 526  
 527  #  define GC_self_thread_inner() GC_lookup_thread(thread_id_self())
 528  
 529  /*
 530   * Wait until an in-progress collection has finished.
 531   * We hold the allocator lock and repeatedly release it in order to wait.
 532   * If `wait_for_all`, then we exit with the allocator lock held and
 533   * no collection is in progress; otherwise we just wait for the current
 534   * collection to finish.
 535   */
 536  GC_INNER void GC_wait_for_gc_completion(GC_bool wait_for_all);
 537  
 538  #  ifdef NACL
 539  GC_INNER void GC_nacl_initialize_gc_thread(GC_thread);
 540  GC_INNER void GC_nacl_shutdown_gc_thread(void);
 541  #  endif
 542  
 543  #  if defined(PTHREAD_STOP_WORLD_IMPL)            \
 544            && !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) \
 545        || defined(GC_EXPLICIT_SIGNALS_UNBLOCK)
 546  /*
 547   * Some targets (e.g., Solaris) might require this to be called when
 548   * doing thread registering from the thread destructor.
 549   */
 550  GC_INNER void GC_unblock_gc_signals(void);
 551  #  endif
 552  
 553  #  if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD)
 554  GC_INNER void GC_suspend_self_inner(GC_thread me, size_t suspend_cnt);
 555  
 556  /*
 557   * A wrapper over `GC_suspend_self_inner()`.  Similar to
 558   * `GC_do_blocking_inner()` but assuming the allocator lock is held and
 559   * `fn` is `GC_suspend_self_inner`.
 560   */
 561  GC_INNER void GC_suspend_self_blocked(ptr_t thread_me, void *context);
 562  #  endif
 563  
 564  #  if defined(GC_PTHREADS) && !defined(PLATFORM_THREADS) \
 565        && !defined(SN_TARGET_PSP2)
 566  
 567  #    ifdef GC_PTHREAD_START_STANDALONE
 568  #      define GC_INNER_PTHRSTART /*< empty */
 569  #    else
 570  #      define GC_INNER_PTHRSTART GC_INNER
 571  #    endif
 572  
 573  GC_INNER_PTHRSTART void *GC_CALLBACK
 574  GC_pthread_start_inner(struct GC_stack_base *sb, void *arg);
 575  
 576  /*
 577   * Called from `GC_pthread_start_inner()`.  Defined in `pthread_support.c`
 578   * file to minimize the number of files included from `pthread_start.c` file
 579   * (because `sem_t` and `sem_post()` are not used in that file directly).
 580   */
 581  GC_INNER_PTHRSTART GC_thread
 582  GC_start_rtn_prepare_thread(void *(**pstart)(void *), void **pstart_arg,
 583                              struct GC_stack_base *sb, void *arg);
 584  
 585  /*
 586   * Called at thread exit.  Never called for main thread.  That is OK,
 587   * since it results in at most a tiny one-time leak.  And LinuxThreads
 588   * implementation does not reclaim the primordial (main) thread resources
 589   * or id anyway.
 590   */
 591  GC_INNER_PTHRSTART void GC_thread_exit_proc(void *);
 592  #  endif /* GC_PTHREADS */
 593  
 594  #  ifdef DARWIN
 595  #    ifndef DARWIN_DONT_PARSE_STACK
 596  GC_INNER ptr_t GC_FindTopOfStack(unsigned long);
 597  #    endif
 598  #    if defined(PARALLEL_MARK) && !defined(GC_NO_THREADS_DISCOVERY)
 599  /* Note: this is used only by `GC_suspend_thread_list()`. */
 600  GC_INNER GC_bool GC_is_mach_marker(thread_act_t);
 601  #    endif
 602  #  endif /* DARWIN */
 603  
 604  #  ifdef PTHREAD_STOP_WORLD_IMPL
 605  GC_INNER void GC_stop_init(void);
 606  #  endif
 607  
 608  EXTERN_C_END
 609  
 610  #endif /* THREADS */
 611  
 612  #endif /* GC_PTHREAD_SUPPORT_H */
 613