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