source: mainline/libc/generic/psthread.c@ 9a71438

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9a71438 was 63bb83e, checked in by Ondrej Palkovsky <ondrap@…>, 20 years ago

Fixed bad TLS BSS initialization.

  • Property mode set to 100644
File size: 7.9 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#include <libadt/list.h>
30#include <psthread.h>
31#include <malloc.h>
32#include <unistd.h>
33#include <thread.h>
34#include <stdio.h>
35#include <kernel/arch/faddr.h>
36#include <futex.h>
37#include <assert.h>
38#include <async.h>
39
40#ifndef PSTHREAD_INITIAL_STACK_PAGES_NO
41#define PSTHREAD_INITIAL_STACK_PAGES_NO 1
42#endif
43
44static LIST_INITIALIZE(ready_list);
45static LIST_INITIALIZE(serialized_list);
46static LIST_INITIALIZE(manager_list);
47
48static void psthread_exit(void) __attribute__ ((noinline));
49static void psthread_main(void);
50
51static atomic_t psthread_futex = FUTEX_INITIALIZER;
52/** Count of real threads that are in async_serialized mode */
53static int serialized_threads; /* Protected by async_futex */
54/** Thread-local count of serialization. If >0, we must not preempt */
55static __thread int serialization_count;
56/** Counter of threads residing in async_manager */
57static int threads_in_manager;
58
59/** Setup PSthread information into TCB structure */
60psthread_data_t * psthread_setup()
61{
62 psthread_data_t *pt;
63 tcb_t *tcb;
64
65 tcb = __make_tls();
66 if (!tcb)
67 return NULL;
68
69 pt = malloc(sizeof(*pt));
70 if (!pt) {
71 __free_tls(tcb);
72 return NULL;
73 }
74
75 tcb->pst_data = pt;
76 pt->tcb = tcb;
77
78 return pt;
79}
80
81void psthread_teardown(psthread_data_t *pt)
82{
83 __free_tls(pt->tcb);
84 free(pt);
85}
86
87/** Function that is called on entry to new uspace thread */
88void psthread_main(void)
89{
90 psthread_data_t *pt = __tcb_get()->pst_data;
91
92 pt->retval = pt->func(pt->arg);
93
94 pt->finished = 1;
95 if (pt->waiter)
96 list_append(&pt->waiter->link, &ready_list);
97
98 psthread_schedule_next_adv(PS_FROM_DEAD);
99}
100
101/** Schedule next userspace pseudo thread.
102 *
103 * If calling with PS_TO_MANAGER parameter, the async_futex should be
104 * held.
105 *
106 * @param tomanager If true, we are switching to next ready manager thread
107 * (if none is found, thread is exited)
108 * @param frommanager If true, we are switching from manager thread
109 * @return 0 if there is no ready pseudo thread, 1 otherwise.
110 */
111int psthread_schedule_next_adv(pschange_type ctype)
112{
113 psthread_data_t *srcpt, *dstpt;
114 int retval = 0;
115
116 futex_down(&psthread_futex);
117
118 if (ctype == PS_PREEMPT && list_empty(&ready_list))
119 goto ret_0;
120
121 if (ctype == PS_FROM_MANAGER) {
122 if (list_empty(&ready_list) && list_empty(&serialized_list))
123 goto ret_0;
124 /* Do not preempt if there is not sufficient count of thread managers */
125 if (list_empty(&serialized_list) && threads_in_manager <= serialized_threads) {
126 goto ret_0;
127 }
128 }
129 /* If we are going to manager and none exists, create it */
130 if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
131 while (list_empty(&manager_list)) {
132 futex_up(&psthread_futex);
133 async_create_manager();
134 futex_down(&psthread_futex);
135 }
136 }
137
138 if (ctype != PS_FROM_DEAD) {
139 /* Save current state */
140 srcpt = __tcb_get()->pst_data;
141 if (!context_save(&srcpt->ctx)) {
142 if (serialization_count)
143 srcpt->flags &= ~PSTHREAD_SERIALIZED;
144 return 1; // futex_up already done here
145 }
146
147 /* Save myself to correct run list */
148 if (ctype == PS_PREEMPT)
149 list_append(&srcpt->link, &ready_list);
150 else if (ctype == PS_FROM_MANAGER) {
151 list_append(&srcpt->link, &manager_list);
152 threads_in_manager--;
153 } /* If ctype == PS_TO_MANAGER, don't save ourselves to any list, we should
154 * already be somewhere, or we will be lost */
155 }
156
157 /* Choose new thread to run */
158 if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
159 dstpt = list_get_instance(manager_list.next,psthread_data_t, link);
160 if (serialization_count && ctype == PS_TO_MANAGER) {
161 serialized_threads++;
162 srcpt->flags |= PSTHREAD_SERIALIZED;
163 }
164 threads_in_manager++;
165 } else {
166 if (!list_empty(&serialized_list)) {
167 dstpt = list_get_instance(serialized_list.next, psthread_data_t, link);
168 serialized_threads--;
169 } else
170 dstpt = list_get_instance(ready_list.next, psthread_data_t, link);
171 }
172 list_remove(&dstpt->link);
173
174 futex_up(&psthread_futex);
175 context_restore(&dstpt->ctx);
176
177ret_0:
178 futex_up(&psthread_futex);
179 return retval;
180}
181
182/** Wait for uspace pseudo thread to finish.
183 *
184 * @param psthrid Pseudo thread to wait for.
185 *
186 * @return Value returned by the finished thread.
187 */
188int psthread_join(pstid_t psthrid)
189{
190 volatile psthread_data_t *pt, *mypt;
191 volatile int retval;
192
193 /* Handle psthrid = Kernel address -> it is wait for call */
194 pt = (psthread_data_t *) psthrid;
195
196 /* TODO */
197 printf("join unsupported\n");
198 _exit(1);
199
200 retval = pt->retval;
201
202 free(pt->stack);
203 psthread_teardown((void *)pt);
204
205 return retval;
206}
207
208/**
209 * Create a userspace thread
210 *
211 * @param func Pseudo thread function.
212 * @param arg Argument to pass to func.
213 *
214 * @return 0 on failure, TLS of the new pseudo thread.
215 */
216pstid_t psthread_create(int (*func)(void *), void *arg)
217{
218 psthread_data_t *pt;
219
220 pt = psthread_setup();
221 if (!pt)
222 return 0;
223 pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize());
224
225 if (!pt->stack) {
226 psthread_teardown(pt);
227 return 0;
228 }
229
230 pt->arg= arg;
231 pt->func = func;
232 pt->finished = 0;
233 pt->waiter = NULL;
234 pt->flags = 0;
235
236 context_save(&pt->ctx);
237 context_set(&pt->ctx, FADDR(psthread_main), pt->stack, PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize(),
238 pt->tcb);
239
240 return (pstid_t )pt;
241}
242
243/** Add a thread to ready list */
244void psthread_add_ready(pstid_t psthrid)
245{
246 psthread_data_t *pt;
247
248 pt = (psthread_data_t *) psthrid;
249 futex_down(&psthread_futex);
250 if ((pt->flags & PSTHREAD_SERIALIZED))
251 list_append(&pt->link, &serialized_list);
252 else
253 list_append(&pt->link, &ready_list);
254 futex_up(&psthread_futex);
255}
256
257/** Add a thread to manager list */
258void psthread_add_manager(pstid_t psthrid)
259{
260 psthread_data_t *pt;
261
262 pt = (psthread_data_t *) psthrid;
263
264 futex_down(&psthread_futex);
265 list_append(&pt->link, &manager_list);
266 futex_up(&psthread_futex);
267}
268
269/** Remove one manager from manager list */
270void psthread_remove_manager()
271{
272 futex_down(&psthread_futex);
273 if (list_empty(&manager_list)) {
274 futex_up(&psthread_futex);
275 return;
276 }
277 list_remove(manager_list.next);
278 futex_up(&psthread_futex);
279}
280
281/** Return thread id of current running thread */
282pstid_t psthread_get_id(void)
283{
284 return (pstid_t)__tcb_get()->pst_data;
285}
286
287/** Disable preemption
288 *
289 * If the thread wants to send several message in row and does not want
290 * to be preempted, it should start async_serialize_start() in the beginning
291 * of communication and async_serialize_end() in the end. If it is a
292 * true multithreaded application, it should protect the communication channel
293 * by a futex as well. Interrupt messages will can still be preempted.
294 */
295void psthread_inc_sercount(void)
296{
297 serialization_count++;
298}
299
300void psthread_dec_sercount(void)
301{
302 serialization_count--;
303}
Note: See TracBrowser for help on using the repository browser.