...

Text file src/runtime/cgo/pthread_unix.c

Documentation: runtime/cgo

     1// Copyright 2025 The Go Authors. All rights reserved.
     2// Use of this source code is governed by a BSD-style
     3// license that can be found in the LICENSE file.
     4
     5//go:build unix
     6
     7#ifndef _GNU_SOURCE // pthread_getattr_np
     8#define _GNU_SOURCE
     9#endif
    10
    11#include <pthread.h>
    12#include <string.h>
    13#include <signal.h>
    14#include <errno.h>
    15#include "libcgo.h"
    16#include "libcgo_unix.h"
    17
    18void
    19_cgo_sys_thread_start(ThreadStart *ts)
    20{
    21	pthread_attr_t attr;
    22	sigset_t ign, oset;
    23	pthread_t p;
    24	size_t size;
    25	int err;
    26
    27	sigfillset(&ign);
    28	pthread_sigmask(SIG_SETMASK, &ign, &oset);
    29
    30	pthread_attr_init(&attr);
    31	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    32#if defined(__APPLE__)
    33	// Copy stack size from parent thread instead of using the
    34	// non-main thread default stack size.
    35	size = pthread_get_stacksize_np(pthread_self());
    36	pthread_attr_setstacksize(&attr, size);
    37#else
    38	pthread_attr_getstacksize(&attr, &size);
    39#endif
    40
    41#if defined(__sun)
    42	// Solaris can report 0 stack size, fix it.
    43	if (size == 0) {
    44		size = 2 << 20;
    45		if (pthread_attr_setstacksize(&attr, size) != 0) {
    46			perror("runtime/cgo: pthread_attr_setstacksize failed");
    47		}
    48	}
    49#endif
    50
    51	// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
    52	ts->g->stackhi = size;
    53	err = _cgo_try_pthread_create(&p, &attr, threadentry, ts);
    54
    55	pthread_sigmask(SIG_SETMASK, &oset, nil);
    56
    57	if (err != 0) {
    58		fatalf("pthread_create failed: %s", strerror(err));
    59	}
    60}
    61
    62void
    63x_cgo_sys_thread_create(void* (*func)(void*)) {
    64	pthread_attr_t attr;
    65	pthread_t p;
    66	int err;
    67
    68	pthread_attr_init(&attr);
    69	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    70	err = _cgo_try_pthread_create(&p, &attr, func, NULL);
    71	if (err != 0) {
    72		fatalf("pthread_create failed: %s", strerror(err));
    73	}
    74}
    75
    76void
    77x_cgo_getstackbound(uintptr bounds[2])
    78{
    79	pthread_attr_t attr;
    80	void *addr;
    81	size_t size;
    82
    83	// Needed before pthread_getattr_np, too, since before glibc 2.32
    84	// it did not call pthread_attr_init in all cases (see #65625).
    85	pthread_attr_init(&attr);
    86#if defined(__APPLE__)
    87	// On macOS/iOS, use the non-portable pthread_get_stackaddr_np
    88	// and pthread_get_stacksize_np APIs (high address + size).
    89	addr = pthread_get_stackaddr_np(pthread_self());
    90	size = pthread_get_stacksize_np(pthread_self());
    91	addr = (void*)((uintptr)addr - size); // convert to low address
    92#elif defined(__GLIBC__) || defined(__BIONIC__) || (defined(__sun) && !defined(__illumos__))
    93	// pthread_getattr_np is a GNU extension supported in glibc.
    94	// Solaris is not glibc but does support pthread_getattr_np
    95	// (and the fallback doesn't work...). Illumos does not.
    96	pthread_getattr_np(pthread_self(), &attr);  // GNU extension
    97	pthread_attr_getstack(&attr, &addr, &size); // low address
    98#elif defined(__illumos__)
    99	pthread_attr_get_np(pthread_self(), &attr);
   100	pthread_attr_getstack(&attr, &addr, &size); // low address
   101#else
   102	// We don't know how to get the current stacks, leave it as
   103	// 0 and the caller will use an estimate based on the current
   104	// SP.
   105	addr = 0;
   106	size = 0;
   107#endif
   108	pthread_attr_destroy(&attr);
   109
   110	// bounds points into the Go stack. TSAN can't see the synchronization
   111	// in Go around stack reuse.
   112	_cgo_tsan_acquire();
   113	bounds[0] = (uintptr)addr;
   114	bounds[1] = (uintptr)addr + size;
   115	_cgo_tsan_release();
   116}
   117
   118// _cgo_try_pthread_create retries pthread_create if it fails with EAGAIN.
   119int
   120_cgo_try_pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*pfn)(void*), void* arg) {
   121	int tries;
   122	int err;
   123	struct timespec ts;
   124
   125	for (tries = 0; tries < 20; tries++) {
   126		err = pthread_create(thread, attr, pfn, arg);
   127		if (err == 0) {
   128			return 0;
   129		}
   130		if (err != EAGAIN) {
   131			return err;
   132		}
   133		ts.tv_sec = 0;
   134		ts.tv_nsec = (tries + 1) * 1000 * 1000; // Milliseconds.
   135		nanosleep(&ts, nil);
   136	}
   137	return EAGAIN;
   138}

View as plain text