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