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