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
26static 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
35static pthread_thread sMainThread;
36static int sConcurrencyLevel;
37
38
39static status_t
40pthread_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
54void
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
76pthread_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
89void
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
103status_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
136void
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
146int
147pthread_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
180pthread_t
181pthread_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
191int
192pthread_equal(pthread_t t1, pthread_t t2)
193{
194	return t1 == t2;
195}
196
197
198int
199pthread_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
220void
221pthread_exit(void* value)
222{
223	pthread_self()->exit_value = value;
224	exit_thread(B_OK);
225}
226
227
228int
229pthread_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
243int
244pthread_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
262int
263pthread_getconcurrency(void)
264{
265	return sConcurrencyLevel;
266}
267
268
269int
270pthread_setconcurrency(int newLevel)
271{
272	if (newLevel < 0)
273		return EINVAL;
274
275	sConcurrencyLevel = newLevel;
276	return 0;
277}
278
279
280int
281pthread_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		if (info.priority >= B_FIRST_REAL_TIME_PRIORITY)
290			*policy = SCHED_RR;
291		else
292			*policy = SCHED_OTHER;
293	}
294	return 0;
295}
296
297
298int
299pthread_setschedparam(pthread_t thread, int policy,
300	const struct sched_param *param)
301{
302	status_t status;
303	if (policy != SCHED_RR && policy != SCHED_OTHER)
304		return ENOTSUP;
305	if (policy == SCHED_RR && param->sched_priority < B_FIRST_REAL_TIME_PRIORITY)
306		return EINVAL;
307	if (policy == SCHED_OTHER && param->sched_priority >= B_FIRST_REAL_TIME_PRIORITY)
308		return EINVAL;
309	status = _kern_set_thread_priority(thread->id, param->sched_priority);
310	if (status == B_BAD_THREAD_ID)
311		return ESRCH;
312	if (status < B_OK)
313		return status;
314	return 0;
315}
316
317
318// #pragma mark - Haiku thread API bridge
319
320
321thread_id
322get_pthread_thread_id(pthread_t thread)
323{
324	return thread->id;
325}
326
327
328int
329__pthread_getname_np(pthread_t thread, char* buffer, size_t length)
330{
331    thread_info info;
332    status_t status = _kern_get_thread_info(thread->id, &info);
333    if (status == B_BAD_THREAD_ID)
334        return ESRCH;
335    if (strlcpy(buffer, info.name, length) >= length)
336        return ERANGE;
337    return 0;
338}
339
340
341int
342__pthread_setname_np(pthread_t thread, const char* name)
343{
344    status_t status = _kern_rename_thread(thread->id, name);
345    if (status == B_BAD_THREAD_ID)
346        return ESRCH;
347    if (status < B_OK)
348        return status;
349    return 0;
350}
351
352B_DEFINE_WEAK_ALIAS(__pthread_getname_np, pthread_getname_np);
353B_DEFINE_WEAK_ALIAS(__pthread_setname_np, pthread_setname_np);
354