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