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*), void* arg) {
    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, arg);
    71  	if (err != 0) {
    72  		fatalf("pthread_create failed: %s", strerror(err));
    73  	}
    74  }
    75  
    76  void
    77  x_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.
   119  int
   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  }
   139  

View as plain text