source: mainline/uspace/lib/libc/generic/psthread.c@ b9641ee

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b9641ee was b9641ee, checked in by Jakub Jermar <jakub@…>, 18 years ago
  1. Implement simple joining of pseudo threads. Only one pseudo thread is allowed to join another pseudo

thread. The restriction is that both pseudo threads must be from the same thread (this is to simplify
synchronization). The joiner doesn't free anything from the joinee. It only gets its return value. Not
tested thoroughly yet.

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