source: mainline/uspace/libc/generic/psthread.c@ fadd381

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fadd381 was fadd381, checked in by Jakub Jermar <jakub@…>, 19 years ago

Indentation and formatting facelift for libc.
Libc headers now have C99 compliant guards.

  • Property mode set to 100644
File size: 7.8 KB
Line 
1/*
2 * Copyright (C) 2006 Ondrej Palkovsky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/** @file
33 */
34
35#include <libadt/list.h>
36#include <psthread.h>
37#include <malloc.h>
38#include <unistd.h>
39#include <thread.h>
40#include <stdio.h>
41#include <libarch/faddr.h>
42#include <futex.h>
43#include <assert.h>
44#include <async.h>
45
46#ifndef PSTHREAD_INITIAL_STACK_PAGES_NO
47#define PSTHREAD_INITIAL_STACK_PAGES_NO 1
48#endif
49
50static LIST_INITIALIZE(ready_list);
51static LIST_INITIALIZE(serialized_list);
52static LIST_INITIALIZE(manager_list);
53
54static void psthread_main(void);
55
56static atomic_t psthread_futex = FUTEX_INITIALIZER;
57/** Count of real threads that are in async_serialized mode */
58static int serialized_threads; /* Protected by async_futex */
59/** Thread-local count of serialization. If >0, we must not preempt */
60static __thread int serialization_count;
61/** Counter of threads residing in async_manager */
62static int threads_in_manager;
63
64/** Setup PSthread information into TCB structure */
65psthread_data_t * psthread_setup()
66{
67 psthread_data_t *pt;
68 tcb_t *tcb;
69
70 tcb = __make_tls();
71 if (!tcb)
72 return NULL;
73
74 pt = malloc(sizeof(*pt));
75 if (!pt) {
76 __free_tls(tcb);
77 return NULL;
78 }
79
80 tcb->pst_data = pt;
81 pt->tcb = tcb;
82
83 return pt;
84}
85
86void psthread_teardown(psthread_data_t *pt)
87{
88 __free_tls(pt->tcb);
89 free(pt);
90}
91
92/** Function that is called on entry to new uspace thread */
93void psthread_main(void)
94{
95 psthread_data_t *pt = __tcb_get()->pst_data;
96
97 pt->retval = pt->func(pt->arg);
98
99 pt->finished = 1;
100 if (pt->waiter)
101 list_append(&pt->waiter->link, &ready_list);
102
103 psthread_schedule_next_adv(PS_FROM_DEAD);
104}
105
106/** Schedule next userspace pseudo thread.
107 *
108 * If calling with PS_TO_MANAGER parameter, the async_futex should be
109 * held.
110 *
111 * @param ctype Type of switch.
112 * @return 0 if there is no ready pseudo thread, 1 otherwise.
113 */
114int psthread_schedule_next_adv(pschange_type ctype)
115{
116 psthread_data_t *srcpt, *dstpt;
117 int retval = 0;
118
119 futex_down(&psthread_futex);
120
121 if (ctype == PS_PREEMPT && list_empty(&ready_list))
122 goto ret_0;
123
124 if (ctype == PS_FROM_MANAGER) {
125 if (list_empty(&ready_list) && list_empty(&serialized_list))
126 goto ret_0;
127 /* Do not preempt if there is not sufficient count of thread managers */
128 if (list_empty(&serialized_list) && threads_in_manager <= serialized_threads) {
129 goto ret_0;
130 }
131 }
132 /* If we are going to manager and none exists, create it */
133 if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
134 while (list_empty(&manager_list)) {
135 futex_up(&psthread_futex);
136 async_create_manager();
137 futex_down(&psthread_futex);
138 }
139 }
140
141 if (ctype != PS_FROM_DEAD) {
142 /* Save current state */
143 srcpt = __tcb_get()->pst_data;
144 if (!context_save(&srcpt->ctx)) {
145 if (serialization_count)
146 srcpt->flags &= ~PSTHREAD_SERIALIZED;
147 return 1; // futex_up already done here
148 }
149
150 /* Save myself to correct run list */
151 if (ctype == PS_PREEMPT)
152 list_append(&srcpt->link, &ready_list);
153 else if (ctype == PS_FROM_MANAGER) {
154 list_append(&srcpt->link, &manager_list);
155 threads_in_manager--;
156 } /* If ctype == PS_TO_MANAGER, don't save ourselves to any list, we should
157 * already be somewhere, or we will be lost */
158 } else
159 srcpt = NULL; /* Avoid GCC warning, if ctype == PS_FROM_DEAD, srcpt is not used */
160
161 /* Choose new thread to run */
162 if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
163 dstpt = list_get_instance(manager_list.next,psthread_data_t, link);
164 if (serialization_count && ctype == PS_TO_MANAGER) {
165 serialized_threads++;
166 srcpt->flags |= PSTHREAD_SERIALIZED;
167 }
168 threads_in_manager++;
169 } else {
170 if (!list_empty(&serialized_list)) {
171 dstpt = list_get_instance(serialized_list.next, psthread_data_t, link);
172 serialized_threads--;
173 } else
174 dstpt = list_get_instance(ready_list.next, psthread_data_t, link);
175 }
176 list_remove(&dstpt->link);
177
178 futex_up(&psthread_futex);
179 context_restore(&dstpt->ctx);
180
181ret_0:
182 futex_up(&psthread_futex);
183 return retval;
184}
185
186/** Wait for uspace pseudo thread to finish.
187 *
188 * @param psthrid Pseudo thread to wait for.
189 *
190 * @return Value returned by the finished thread.
191 */
192int psthread_join(pstid_t psthrid)
193{
194 volatile psthread_data_t *pt;
195 volatile int retval;
196
197 /* Handle psthrid = Kernel address -> it is wait for call */
198 pt = (psthread_data_t *) psthrid;
199
200 /* TODO */
201 printf("join unsupported\n");
202 _exit(1);
203
204 retval = pt->retval;
205
206 free(pt->stack);
207 psthread_teardown((void *)pt);
208
209 return retval;
210}
211
212/**
213 * Create a userspace thread
214 *
215 * @param func Pseudo thread function.
216 * @param arg Argument to pass to func.
217 *
218 * @return 0 on failure, TLS of the new pseudo thread.
219 */
220pstid_t psthread_create(int (*func)(void *), void *arg)
221{
222 psthread_data_t *pt;
223
224 pt = psthread_setup();
225 if (!pt)
226 return 0;
227 pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize());
228
229 if (!pt->stack) {
230 psthread_teardown(pt);
231 return 0;
232 }
233
234 pt->arg= arg;
235 pt->func = func;
236 pt->finished = 0;
237 pt->waiter = NULL;
238 pt->flags = 0;
239
240 context_save(&pt->ctx);
241 context_set(&pt->ctx, FADDR(psthread_main), pt->stack, PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize(), pt->tcb);
242
243 return (pstid_t )pt;
244}
245
246/** Add a thread to ready list */
247void psthread_add_ready(pstid_t psthrid)
248{
249 psthread_data_t *pt;
250
251 pt = (psthread_data_t *) psthrid;
252 futex_down(&psthread_futex);
253 if ((pt->flags & PSTHREAD_SERIALIZED))
254 list_append(&pt->link, &serialized_list);
255 else
256 list_append(&pt->link, &ready_list);
257 futex_up(&psthread_futex);
258}
259
260/** Add a thread to manager list */
261void psthread_add_manager(pstid_t psthrid)
262{
263 psthread_data_t *pt;
264
265 pt = (psthread_data_t *) psthrid;
266
267 futex_down(&psthread_futex);
268 list_append(&pt->link, &manager_list);
269 futex_up(&psthread_futex);
270}
271
272/** Remove one manager from manager list */
273void psthread_remove_manager()
274{
275 futex_down(&psthread_futex);
276 if (list_empty(&manager_list)) {
277 futex_up(&psthread_futex);
278 return;
279 }
280 list_remove(manager_list.next);
281 futex_up(&psthread_futex);
282}
283
284/** Return thread id of current running thread */
285pstid_t psthread_get_id(void)
286{
287 return (pstid_t)__tcb_get()->pst_data;
288}
289
290/** Disable preemption
291 *
292 * If the thread wants to send several message in row and does not want
293 * to be preempted, it should start async_serialize_start() in the beginning
294 * of communication and async_serialize_end() in the end. If it is a
295 * true multithreaded application, it should protect the communication channel
296 * by a futex as well. Interrupt messages will can still be preempted.
297 */
298void psthread_inc_sercount(void)
299{
300 serialization_count++;
301}
302
303void psthread_dec_sercount(void)
304{
305 serialization_count--;
306}
307
308/** @}
309 */
Note: See TracBrowser for help on using the repository browser.