xref: /haiku/src/system/libroot/posix/pthread/pthread.cpp (revision caed67a8cba83913b9c21ac2b06ebc6bd1cb3111)
1 /*
2  * Copyright 2024, Haiku, Inc. All rights reserved.
3  * Copyright 2008-2009, Axel Dörfler, axeld@pinc-software.de.
4  * Copyright 2006, Jérôme Duval. All rights reserved.
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "pthread_private.h"
10 
11 #include <signal.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <TLS.h>
16 
17 #include <syscall_utils.h>
18 
19 #include <libroot_private.h>
20 #include <syscalls.h>
21 #include <thread_defs.h>
22 #include <time_private.h>
23 #include <tls.h>
24 
25 #include <user_thread.h>
26 
27 
28 static pthread_attr pthread_attr_default = {
29 	PTHREAD_CREATE_JOINABLE,
30 	B_NORMAL_PRIORITY,
31 	USER_STACK_SIZE,
32 	USER_STACK_GUARD_SIZE,
33 	NULL
34 };
35 
36 
37 static pthread_thread sMainThread;
38 static int sConcurrencyLevel;
39 
40 
41 static status_t
42 pthread_thread_entry(void*, void* _thread)
43 {
44 	pthread_thread* thread = (pthread_thread*)_thread;
45 
46 	__heap_thread_init();
47 
48 	pthread_exit(thread->entry(thread->entry_argument));
49 	return 0;
50 }
51 
52 
53 // #pragma mark - private API
54 
55 
56 void
57 __pthread_destroy_thread(void)
58 {
59 	pthread_thread* thread = pthread_self();
60 
61 	// call cleanup handlers
62 	while (true) {
63 		struct __pthread_cleanup_handler* handler
64 			= __pthread_cleanup_pop_handler();
65 		if (handler == NULL)
66 			break;
67 
68 		handler->function(handler->argument);
69 	}
70 
71 	__pthread_key_call_destructors(thread);
72 
73 	if ((atomic_or(&thread->flags, THREAD_DEAD) & THREAD_DETACHED) != 0)
74 		free(thread);
75 }
76 
77 
78 pthread_thread*
79 __allocate_pthread(void* (*entry)(void*), void *data)
80 {
81 	pthread_thread* thread = (pthread_thread*)malloc(sizeof(pthread_thread));
82 	if (thread == NULL)
83 		return NULL;
84 
85 	__init_pthread(thread, entry, data);
86 
87 	return thread;
88 }
89 
90 
91 void
92 __init_pthread(pthread_thread* thread, void* (*entry)(void*), void* data)
93 {
94 	thread->entry = entry;
95 	thread->entry_argument = data;
96 	thread->exit_value = NULL;
97 	thread->cleanup_handlers = NULL;
98 	thread->flags = THREAD_CANCEL_ENABLED;
99 		// thread cancellation enabled, but deferred
100 
101 	memset(thread->specific, 0, sizeof(thread->specific));
102 }
103 
104 
105 status_t
106 __pthread_init_creation_attributes(const pthread_attr_t* pthreadAttributes,
107 	pthread_t thread, status_t (*entryFunction)(void*, void*),
108 	void* argument1, void* argument2, const char* name,
109 	thread_creation_attributes* attributes)
110 {
111 	const pthread_attr* attr = NULL;
112 	if (pthreadAttributes == NULL) {
113 		attr = &pthread_attr_default;
114 	} else {
115 		attr = *pthreadAttributes;
116 		if (attr == NULL)
117 			return EINVAL;
118 	}
119 
120 	attributes->entry = entryFunction;
121 	attributes->name = name;
122 	attributes->priority = attr->sched_priority;
123 	attributes->args1 = argument1;
124 	attributes->args2 = argument2;
125 	attributes->stack_address = attr->stack_address;
126 	attributes->stack_size = attr->stack_size;
127 	attributes->guard_size = attr->guard_size;
128 	attributes->pthread = thread;
129 	attributes->flags = 0;
130 
131 	if (thread != NULL && attr->detach_state == PTHREAD_CREATE_DETACHED)
132 		thread->flags |= THREAD_DETACHED;
133 
134 	return B_OK;
135 }
136 
137 
138 void
139 __pthread_set_default_priority(int32 priority)
140 {
141 	pthread_attr_default.sched_priority = priority;
142 }
143 
144 
145 static int
146 __pthread_join(pthread_t thread, void** _value, int flags = 0, bigtime_t timeout = 0)
147 {
148 	status_t status;
149 	do {
150 		status_t dummy;
151 		status = wait_for_thread_etc(thread->id, flags, timeout, &dummy);
152 	} while (status == B_INTERRUPTED);
153 
154 	if (status == B_BAD_THREAD_ID)
155 		RETURN_AND_TEST_CANCEL(ESRCH);
156 	if (status == B_WOULD_BLOCK || status == B_TIMED_OUT)
157 		RETURN_AND_TEST_CANCEL(ETIMEDOUT);
158 	if (status < B_OK)
159 		RETURN_AND_TEST_CANCEL(status);
160 
161 	if (_value != NULL)
162 		*_value = thread->exit_value;
163 
164 	if ((atomic_or(&thread->flags, THREAD_DETACHED) & THREAD_DEAD) != 0)
165 		free(thread);
166 
167 	RETURN_AND_TEST_CANCEL(B_OK);
168 }
169 
170 
171 // #pragma mark - public API
172 
173 
174 int
175 pthread_create(pthread_t* _thread, const pthread_attr_t* attr,
176 	void* (*startRoutine)(void*), void* arg)
177 {
178 	if (_thread == NULL)
179 		return EINVAL;
180 
181 	pthread_thread* thread = __allocate_pthread(startRoutine, arg);
182 	if (thread == NULL)
183 		return EAGAIN;
184 
185 	thread_creation_attributes attributes;
186 	status_t error = __pthread_init_creation_attributes(attr, thread,
187 		&pthread_thread_entry, NULL, thread, "pthread func", &attributes);
188 	if (error != B_OK) {
189 		free(thread);
190 		return error;
191 	}
192 
193 	thread->id = _kern_spawn_thread(&attributes);
194 	if (thread->id < 0) {
195 		// stupid error code but demanded by POSIX
196 		free(thread);
197 		return EAGAIN;
198 	}
199 
200 	__set_stack_protection();
201 	*_thread = thread;
202 	resume_thread(thread->id);
203 
204 	return 0;
205 }
206 
207 
208 pthread_t
209 pthread_self(void)
210 {
211 	pthread_thread* thread = get_user_thread()->pthread;
212 	if (thread == NULL)
213 		return &sMainThread;
214 
215 	return thread;
216 }
217 
218 
219 int
220 pthread_equal(pthread_t t1, pthread_t t2)
221 {
222 	return t1 == t2;
223 }
224 
225 
226 int
227 pthread_join(pthread_t thread, void** _value)
228 {
229 	return __pthread_join(thread, _value);
230 }
231 
232 
233 void
234 pthread_exit(void* value)
235 {
236 	pthread_self()->exit_value = value;
237 	exit_thread(B_OK);
238 }
239 
240 
241 int
242 pthread_kill(pthread_t thread, int sig)
243 {
244 	status_t status = send_signal(thread->id, (uint)sig);
245 	if (status != B_OK) {
246 		if (status == B_BAD_THREAD_ID)
247 			return ESRCH;
248 
249 		return status;
250 	}
251 
252 	return 0;
253 }
254 
255 
256 int
257 pthread_detach(pthread_t thread)
258 {
259 	int32 flags;
260 
261 	if (thread == NULL)
262 		return EINVAL;
263 
264 	flags = atomic_or(&thread->flags, THREAD_DETACHED);
265 	if ((flags & THREAD_DETACHED) != 0)
266 		return 0;
267 
268 	if ((flags & THREAD_DEAD) != 0)
269 		free(thread);
270 
271 	return 0;
272 }
273 
274 
275 int
276 pthread_getconcurrency(void)
277 {
278 	return sConcurrencyLevel;
279 }
280 
281 
282 int
283 pthread_setconcurrency(int newLevel)
284 {
285 	if (newLevel < 0)
286 		return EINVAL;
287 
288 	sConcurrencyLevel = newLevel;
289 	return 0;
290 }
291 
292 
293 int
294 pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param)
295 {
296 	thread_info info;
297 	status_t status = _kern_get_thread_info(thread->id, &info);
298 	if (status == B_BAD_THREAD_ID)
299 		return ESRCH;
300 	param->sched_priority = info.priority;
301 	if (policy != NULL) {
302 		if (info.priority >= B_FIRST_REAL_TIME_PRIORITY)
303 			*policy = SCHED_RR;
304 		else
305 			*policy = SCHED_OTHER;
306 	}
307 	return 0;
308 }
309 
310 
311 int
312 pthread_setschedparam(pthread_t thread, int policy,
313 	const struct sched_param *param)
314 {
315 	if (policy != SCHED_RR && policy != SCHED_OTHER)
316 		return ENOTSUP;
317 	if (policy == SCHED_RR && param->sched_priority < B_FIRST_REAL_TIME_PRIORITY)
318 		return EINVAL;
319 	if (policy == SCHED_OTHER && param->sched_priority >= B_FIRST_REAL_TIME_PRIORITY)
320 		return EINVAL;
321 
322 	status_t status = _kern_set_thread_priority(thread->id, param->sched_priority);
323 	if (status == B_BAD_THREAD_ID)
324 		return ESRCH;
325 	if (status < B_OK)
326 		return status;
327 	return 0;
328 }
329 
330 
331 // #pragma mark - extensions
332 
333 
334 extern "C" int
335 pthread_getname_np(pthread_t thread, char* buffer, size_t length)
336 {
337 	thread_info info;
338 	status_t status = _kern_get_thread_info(thread->id, &info);
339 	if (status == B_BAD_THREAD_ID)
340 		return ESRCH;
341 	if (status < B_OK)
342 		return status;
343 	if (strlcpy(buffer, info.name, length) >= length)
344 		return ERANGE;
345 	return 0;
346 }
347 
348 
349 extern "C" int
350 pthread_setname_np(pthread_t thread, const char* name)
351 {
352 	status_t status = _kern_rename_thread(thread->id, name);
353 	if (status == B_BAD_THREAD_ID)
354 		return ESRCH;
355 	if (status < B_OK)
356 		return status;
357 	return 0;
358 }
359 
360 
361 extern "C" int
362 pthread_timedjoin_np(pthread_t thread, void** _value, const struct timespec* abstime)
363 {
364 	int flags = 0;
365 	bigtime_t timeout = 0;
366 	if (abstime != NULL) {
367 		if (!timespec_to_bigtime(*abstime, timeout))
368 			RETURN_AND_TEST_CANCEL(EINVAL);
369 		flags |= B_ABSOLUTE_REAL_TIME_TIMEOUT;
370 	} else {
371 		timeout = 0;
372 		flags |= B_RELATIVE_TIMEOUT;
373 	}
374 
375 	return __pthread_join(thread, _value, flags, timeout);
376 }
377 
378 
379 // #pragma mark - Haiku thread API bridge
380 
381 
382 thread_id
383 get_pthread_thread_id(pthread_t thread)
384 {
385 	return thread->id;
386 }
387