xref: /haiku/src/system/libroot/posix/pthread/pthread.cpp (revision 7457ccb4b2f4786525d3b7bda42598487d57ab7d)
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 const pthread_attr pthread_attr_default = {
27 	PTHREAD_CREATE_JOINABLE,
28 	B_NORMAL_PRIORITY,
29 	USER_STACK_SIZE,
30 	USER_STACK_GUARD_SIZE
31 };
32 
33 
34 static pthread_thread sMainThread;
35 static int sConcurrencyLevel;
36 
37 
38 static status_t
39 pthread_thread_entry(void*, void* _thread)
40 {
41 	pthread_thread* thread = (pthread_thread*)_thread;
42 
43 	pthread_exit(thread->entry(thread->entry_argument));
44 	return 0;
45 }
46 
47 
48 // #pragma mark - private API
49 
50 
51 void
52 __pthread_destroy_thread(void)
53 {
54 	pthread_thread* thread = pthread_self();
55 
56 	// call cleanup handlers
57 	while (true) {
58 		struct __pthread_cleanup_handler* handler
59 			= __pthread_cleanup_pop_handler();
60 		if (handler == NULL)
61 			break;
62 
63 		handler->function(handler->argument);
64 	}
65 
66 	__pthread_key_call_destructors(thread);
67 
68 	if ((atomic_or(&thread->flags, THREAD_DEAD) & THREAD_DETACHED) != 0)
69 		free(thread);
70 }
71 
72 
73 pthread_thread*
74 __allocate_pthread(void* (*entry)(void*), void *data)
75 {
76 	pthread_thread* thread = (pthread_thread*)malloc(sizeof(pthread_thread));
77 	if (thread == NULL)
78 		return NULL;
79 
80 	__init_pthread(thread, entry, data);
81 
82 	return thread;
83 }
84 
85 
86 void
87 __init_pthread(pthread_thread* thread, void* (*entry)(void*), void* data)
88 {
89 	thread->entry = entry;
90 	thread->entry_argument = data;
91 	thread->exit_value = NULL;
92 	thread->cleanup_handlers = NULL;
93 	thread->flags = THREAD_CANCEL_ENABLED;
94 		// thread cancellation enabled, but deferred
95 
96 	memset(thread->specific, 0, sizeof(thread->specific));
97 }
98 
99 
100 status_t
101 __pthread_init_creation_attributes(const pthread_attr_t* pthreadAttributes,
102 	pthread_t thread, status_t (*entryFunction)(void*, void*),
103 	void* argument1, void* argument2, const char* name,
104 	thread_creation_attributes* attributes)
105 {
106 	const pthread_attr* attr = NULL;
107 	if (pthreadAttributes == NULL) {
108 		attr = &pthread_attr_default;
109 	} else {
110 		attr = *pthreadAttributes;
111 		if (attr == NULL)
112 			return EINVAL;
113 	}
114 
115 	attributes->entry = entryFunction;
116 	attributes->name = name;
117 	attributes->priority = attr->sched_priority;
118 	attributes->args1 = argument1;
119 	attributes->args2 = argument2;
120 	attributes->stack_address = NULL;
121 	attributes->stack_size = attr->stack_size;
122 	attributes->guard_size = attr->guard_size;
123 	attributes->pthread = thread;
124 	attributes->flags = 0;
125 
126 	if (thread != NULL && attr->detach_state == PTHREAD_CREATE_DETACHED)
127 		thread->flags |= THREAD_DETACHED;
128 
129 	return B_OK;
130 }
131 
132 
133 // #pragma mark - public API
134 
135 
136 int
137 pthread_create(pthread_t* _thread, const pthread_attr_t* attr,
138 	void* (*startRoutine)(void*), void* arg)
139 {
140 	if (_thread == NULL)
141 		return EINVAL;
142 
143 	pthread_thread* thread = __allocate_pthread(startRoutine, arg);
144 	if (thread == NULL)
145 		return EAGAIN;
146 
147 	thread_creation_attributes attributes;
148 	status_t error = __pthread_init_creation_attributes(attr, thread,
149 		&pthread_thread_entry, NULL, thread, "pthread func", &attributes);
150 	if (error != B_OK) {
151 		free(thread);
152 		return error;
153 	}
154 
155 	thread->id = _kern_spawn_thread(&attributes);
156 	if (thread->id < 0) {
157 		// stupid error code but demanded by POSIX
158 		free(thread);
159 		return EAGAIN;
160 	}
161 
162 	__set_stack_protection();
163 	*_thread = thread;
164 	resume_thread(thread->id);
165 
166 	return 0;
167 }
168 
169 
170 pthread_t
171 pthread_self(void)
172 {
173 	pthread_thread* thread = get_user_thread()->pthread;
174 	if (thread == NULL)
175 		return &sMainThread;
176 
177 	return thread;
178 }
179 
180 
181 int
182 pthread_equal(pthread_t t1, pthread_t t2)
183 {
184 	return t1 == t2;
185 }
186 
187 
188 int
189 pthread_join(pthread_t thread, void** _value)
190 {
191 	status_t dummy;
192 	status_t error;
193 	do {
194 		error = wait_for_thread(thread->id, &dummy);
195 	} while (error == B_INTERRUPTED);
196 
197 	if (error == B_BAD_THREAD_ID)
198 		RETURN_AND_TEST_CANCEL(ESRCH);
199 
200 	if (_value != NULL)
201 		*_value = thread->exit_value;
202 
203 	if ((atomic_or(&thread->flags, THREAD_DETACHED) & THREAD_DEAD) != 0)
204 		free(thread);
205 
206 	RETURN_AND_TEST_CANCEL(error);
207 }
208 
209 
210 void
211 pthread_exit(void* value)
212 {
213 	pthread_self()->exit_value = value;
214 	exit_thread(B_OK);
215 }
216 
217 
218 int
219 pthread_kill(pthread_t thread, int sig)
220 {
221 	status_t status = send_signal(thread->id, (uint)sig);
222 	if (status != B_OK) {
223 		if (status == B_BAD_THREAD_ID)
224 			return ESRCH;
225 
226 		return status;
227 	}
228 
229 	return 0;
230 }
231 
232 
233 int
234 pthread_detach(pthread_t thread)
235 {
236 	int32 flags;
237 
238 	if (thread == NULL)
239 		return EINVAL;
240 
241 	flags = atomic_or(&thread->flags, THREAD_DETACHED);
242 	if ((flags & THREAD_DETACHED) != 0)
243 		return 0;
244 
245 	if ((flags & THREAD_DEAD) != 0)
246 		free(thread);
247 
248 	return 0;
249 }
250 
251 
252 int
253 pthread_getconcurrency(void)
254 {
255 	return sConcurrencyLevel;
256 }
257 
258 
259 int
260 pthread_setconcurrency(int newLevel)
261 {
262 	if (newLevel < 0)
263 		return EINVAL;
264 
265 	sConcurrencyLevel = newLevel;
266 	return 0;
267 }
268 
269 
270 int
271 pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param)
272 {
273 	thread_info info;
274 	status_t status = _kern_get_thread_info(thread->id, &info);
275 	if (status == B_BAD_THREAD_ID)
276 		return ESRCH;
277 	param->sched_priority = info.priority;
278 	if (policy != NULL)
279 		*policy = SCHED_RR;
280 	return 0;
281 }
282 
283 
284 int
285 pthread_setschedparam(pthread_t thread, int policy,
286 	const struct sched_param *param)
287 {
288 	status_t status;
289 	if (policy != SCHED_RR)
290 		return ENOTSUP;
291 	status = _kern_set_thread_priority(thread->id, param->sched_priority);
292 	if (status == B_BAD_THREAD_ID)
293 		return ESRCH;
294 	if (status < B_OK)
295 		return status;
296 	return 0;
297 }
298 
299 
300 // #pragma mark - Haiku thread API bridge
301 
302 
303 thread_id
304 get_pthread_thread_id(pthread_t thread)
305 {
306 	return thread->id;
307 }
308