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

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

New, better-structured, directory layout for uspace.

  • 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 pseudo 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/** Create a userspace pseudo thread.
213 *
214 * @param func Pseudo thread function.
215 * @param arg Argument to pass to func.
216 *
217 * @return 0 on failure, TLS of the new pseudo thread.
218 */
219pstid_t psthread_create(int (*func)(void *), void *arg)
220{
221 psthread_data_t *pt;
222
223 pt = psthread_setup();
224 if (!pt)
225 return 0;
226 pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize());
227
228 if (!pt->stack) {
229 psthread_teardown(pt);
230 return 0;
231 }
232
233 pt->arg= arg;
234 pt->func = func;
235 pt->finished = 0;
236 pt->waiter = NULL;
237 pt->flags = 0;
238
239 context_save(&pt->ctx);
240 context_set(&pt->ctx, FADDR(psthread_main), pt->stack, PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize(), pt->tcb);
241
242 return (pstid_t )pt;
243}
244
245/** Add a thread to ready list */
246void psthread_add_ready(pstid_t psthrid)
247{
248 psthread_data_t *pt;
249
250 pt = (psthread_data_t *) psthrid;
251 futex_down(&psthread_futex);
252 if ((pt->flags & PSTHREAD_SERIALIZED))
253 list_append(&pt->link, &serialized_list);
254 else
255 list_append(&pt->link, &ready_list);
256 futex_up(&psthread_futex);
257}
258
259/** Add a thread to manager list */
260void psthread_add_manager(pstid_t psthrid)
261{
262 psthread_data_t *pt;
263
264 pt = (psthread_data_t *) psthrid;
265
266 futex_down(&psthread_futex);
267 list_append(&pt->link, &manager_list);
268 futex_up(&psthread_futex);
269}
270
271/** Remove one manager from manager list */
272void psthread_remove_manager()
273{
274 futex_down(&psthread_futex);
275 if (list_empty(&manager_list)) {
276 futex_up(&psthread_futex);
277 return;
278 }
279 list_remove(manager_list.next);
280 futex_up(&psthread_futex);
281}
282
283/** Return thread id of current running thread */
284pstid_t psthread_get_id(void)
285{
286 return (pstid_t)__tcb_get()->pst_data;
287}
288
289/** Disable preemption
290 *
291 * If the thread wants to send several message in row and does not want
292 * to be preempted, it should start async_serialize_start() in the beginning
293 * of communication and async_serialize_end() in the end. If it is a
294 * true multithreaded application, it should protect the communication channel
295 * by a futex as well. Interrupt messages will can still be preempted.
296 */
297void psthread_inc_sercount(void)
298{
299 serialization_count++;
300}
301
302void psthread_dec_sercount(void)
303{
304 serialization_count--;
305}
306
307/** @}
308 */
Note: See TracBrowser for help on using the repository browser.