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