Text file src/runtime/cgo/pthread_unix.c

     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  
    18  void
    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  
    62  void
    63  x_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  
    76  void (* _cgo_sys_thread_create)(void* (*func)(void*)) = x_cgo_sys_thread_create;
    77  
    78  void
    79  x_cgo_getstackbound(uintptr bounds[2])
    80  {
    81  	pthread_attr_t attr;
    82  	void *addr;
    83  	size_t size;
    84  
    85  	// Needed before pthread_getattr_np, too, since before glibc 2.32
    86  	// it did not call pthread_attr_init in all cases (see #65625).
    87  	pthread_attr_init(&attr);
    88  #if defined(__APPLE__)
    89  	// On macOS/iOS, use the non-portable pthread_get_stackaddr_np
    90  	// and pthread_get_stacksize_np APIs (high address + size).
    91  	addr = pthread_get_stackaddr_np(pthread_self());
    92  	size = pthread_get_stacksize_np(pthread_self());
    93  	addr = (void*)((uintptr)addr - size); // convert to low address
    94  #elif defined(__GLIBC__) || defined(__BIONIC__) || (defined(__sun) && !defined(__illumos__))
    95  	// pthread_getattr_np is a GNU extension supported in glibc.
    96  	// Solaris is not glibc but does support pthread_getattr_np
    97  	// (and the fallback doesn't work...). Illumos does not.
    98  	pthread_getattr_np(pthread_self(), &attr);  // GNU extension
    99  	pthread_attr_getstack(&attr, &addr, &size); // low address
   100  #elif defined(__illumos__)
   101  	pthread_attr_get_np(pthread_self(), &attr);
   102  	pthread_attr_getstack(&attr, &addr, &size); // low address
   103  #else
   104  	// We don't know how to get the current stacks, leave it as
   105  	// 0 and the caller will use an estimate based on the current
   106  	// SP.
   107  	addr = 0;
   108  	size = 0;
   109  #endif
   110  	pthread_attr_destroy(&attr);
   111  
   112  	// bounds points into the Go stack. TSAN can't see the synchronization
   113  	// in Go around stack reuse.
   114  	_cgo_tsan_acquire();
   115  	bounds[0] = (uintptr)addr;
   116  	bounds[1] = (uintptr)addr + size;
   117  	_cgo_tsan_release();
   118  }
   119  
   120  void (* _cgo_getstackbound)(uintptr[2]) = x_cgo_getstackbound;
   121  
   122  // _cgo_try_pthread_create retries pthread_create if it fails with EAGAIN.
   123  int
   124  _cgo_try_pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*pfn)(void*), void* arg) {
   125  	int tries;
   126  	int err;
   127  	struct timespec ts;
   128  
   129  	for (tries = 0; tries < 20; tries++) {
   130  		err = pthread_create(thread, attr, pfn, arg);
   131  		if (err == 0) {
   132  			return 0;
   133  		}
   134  		if (err != EAGAIN) {
   135  			return err;
   136  		}
   137  		ts.tv_sec = 0;
   138  		ts.tv_nsec = (tries + 1) * 1000 * 1000; // Milliseconds.
   139  		nanosleep(&ts, nil);
   140  	}
   141  	return EAGAIN;
   142  }
   143  

View as plain text