source: mainline/uspace/lib/c/generic/fibril.c@ d73d992

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d73d992 was d73d992, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Hide libc-internal details of the fibril implementation.

  • Property mode set to 100644
File size: 8.5 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 <adt/list.h>
37#include <fibril.h>
38#include <thread.h>
39#include <stack.h>
40#include <tls.h>
41#include <stdlib.h>
42#include <abi/mm/as.h>
43#include <as.h>
44#include <stdio.h>
45#include <libarch/barrier.h>
46#include <context.h>
47#include <futex.h>
48#include <assert.h>
49#include <async.h>
50
51#include "private/fibril.h"
52
53#ifdef FUTEX_UPGRADABLE
54#include <rcu.h>
55#endif
56
57/**
58 * This futex serializes access to ready_list,
59 * manager_list and fibril_list.
60 */
61static futex_t fibril_futex = FUTEX_INITIALIZER;
62
63static LIST_INITIALIZE(ready_list);
64static LIST_INITIALIZE(manager_list);
65static LIST_INITIALIZE(fibril_list);
66
67/** Function that spans the whole life-cycle of a fibril.
68 *
69 * Each fibril begins execution in this function. Then the function implementing
70 * the fibril logic is called. After its return, the return value is saved.
71 * The fibril then switches to another fibril, which cleans up after it.
72 *
73 */
74static void fibril_main(void)
75{
76 /* fibril_futex is locked when a fibril is first started. */
77 futex_unlock(&fibril_futex);
78
79 fibril_t *fibril = fibril_self();
80
81#ifdef FUTEX_UPGRADABLE
82 rcu_register_fibril();
83#endif
84
85 /* Call the implementing function. */
86 fibril->retval = fibril->func(fibril->arg);
87
88 futex_down(&async_futex);
89 fibril_switch(FIBRIL_FROM_DEAD);
90 /* Not reached */
91}
92
93/** Setup fibril information into TCB structure
94 *
95 */
96fibril_t *fibril_setup(void)
97{
98 tcb_t *tcb = tls_make();
99 if (!tcb)
100 return NULL;
101
102 fibril_t *fibril = calloc(1, sizeof(fibril_t));
103 if (!fibril) {
104 tls_free(tcb);
105 return NULL;
106 }
107
108 tcb->fibril_data = fibril;
109 fibril->tcb = tcb;
110
111 /*
112 * We are called before __tcb_set(), so we need to use
113 * futex_down/up() instead of futex_lock/unlock() that
114 * may attempt to access TLS.
115 */
116 futex_down(&fibril_futex);
117 list_append(&fibril->all_link, &fibril_list);
118 futex_up(&fibril_futex);
119
120 return fibril;
121}
122
123void fibril_teardown(fibril_t *fibril, bool locked)
124{
125 if (!locked)
126 futex_lock(&fibril_futex);
127 list_remove(&fibril->all_link);
128 if (!locked)
129 futex_unlock(&fibril_futex);
130 tls_free(fibril->tcb);
131 free(fibril);
132}
133
134/** Switch from the current fibril.
135 *
136 * If stype is FIBRIL_TO_MANAGER or FIBRIL_FROM_DEAD, the async_futex must
137 * be held.
138 *
139 * @param stype Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
140 * FIBRIL_FROM_MANAGER, FIBRIL_FROM_DEAD. The parameter
141 * describes the circumstances of the switch.
142 *
143 * @return 0 if there is no ready fibril,
144 * @return 1 otherwise.
145 *
146 */
147int fibril_switch(fibril_switch_type_t stype)
148{
149 futex_lock(&fibril_futex);
150
151 fibril_t *srcf = fibril_self();
152 fibril_t *dstf = NULL;
153
154 /* Choose a new fibril to run */
155 switch (stype) {
156 case FIBRIL_TO_MANAGER:
157 case FIBRIL_FROM_DEAD:
158 /* Make sure the async_futex is held. */
159 assert((atomic_signed_t) async_futex.val.count <= 0);
160
161 /* If we are going to manager and none exists, create it */
162 while (list_empty(&manager_list)) {
163 futex_unlock(&fibril_futex);
164 async_create_manager();
165 futex_lock(&fibril_futex);
166 }
167
168 dstf = list_get_instance(list_first(&manager_list),
169 fibril_t, link);
170
171 if (stype == FIBRIL_FROM_DEAD)
172 dstf->clean_after_me = srcf;
173 break;
174 case FIBRIL_PREEMPT:
175 case FIBRIL_FROM_MANAGER:
176 if (list_empty(&ready_list)) {
177 futex_unlock(&fibril_futex);
178 return 0;
179 }
180
181 dstf = list_get_instance(list_first(&ready_list), fibril_t,
182 link);
183 break;
184 }
185 list_remove(&dstf->link);
186
187 /* Put the current fibril into the correct run list */
188 switch (stype) {
189 case FIBRIL_PREEMPT:
190 list_append(&srcf->link, &ready_list);
191 break;
192 case FIBRIL_FROM_MANAGER:
193 list_append(&srcf->link, &manager_list);
194 break;
195 case FIBRIL_FROM_DEAD:
196 // Nothing.
197 break;
198 case FIBRIL_TO_MANAGER:
199 /*
200 * Don't put the current fibril into any list, it should
201 * already be somewhere, or it will be lost.
202 */
203 break;
204 }
205
206#ifdef FUTEX_UPGRADABLE
207 if (stype == FIBRIL_FROM_DEAD) {
208 rcu_deregister_fibril();
209 }
210#endif
211
212 /* Swap to the next fibril. */
213 context_swap(&srcf->ctx, &dstf->ctx);
214
215 /* Restored by another fibril! */
216
217 /* Must be after context_swap()! */
218 futex_unlock(&fibril_futex);
219
220 if (srcf->clean_after_me) {
221 /*
222 * Cleanup after the dead fibril from which we
223 * restored context here.
224 */
225 void *stack = srcf->clean_after_me->stack;
226 if (stack) {
227 /*
228 * This check is necessary because a
229 * thread could have exited like a
230 * normal fibril using the
231 * FIBRIL_FROM_DEAD switch type. In that
232 * case, its fibril will not have the
233 * stack member filled.
234 */
235 as_area_destroy(stack);
236 }
237 fibril_teardown(srcf->clean_after_me, true);
238 srcf->clean_after_me = NULL;
239 }
240
241 return 1;
242}
243
244/** Create a new fibril.
245 *
246 * @param func Implementing function of the new fibril.
247 * @param arg Argument to pass to func.
248 * @param stksz Stack size in bytes.
249 *
250 * @return 0 on failure or TLS of the new fibril.
251 *
252 */
253fid_t fibril_create_generic(errno_t (*func)(void *), void *arg, size_t stksz)
254{
255 fibril_t *fibril;
256
257 fibril = fibril_setup();
258 if (fibril == NULL)
259 return 0;
260
261 size_t stack_size = (stksz == FIBRIL_DFLT_STK_SIZE) ?
262 stack_size_get() : stksz;
263 fibril->stack = as_area_create(AS_AREA_ANY, stack_size,
264 AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE | AS_AREA_GUARD |
265 AS_AREA_LATE_RESERVE, AS_AREA_UNPAGED);
266 if (fibril->stack == (void *) -1) {
267 fibril_teardown(fibril, false);
268 return 0;
269 }
270
271 fibril->func = func;
272 fibril->arg = arg;
273
274 context_create_t sctx = {
275 .fn = fibril_main,
276 .stack_base = fibril->stack,
277 .stack_size = stack_size,
278 .tls = fibril->tcb,
279 };
280
281 context_create(&fibril->ctx, &sctx);
282 return (fid_t) fibril;
283}
284
285/** Delete a fibril that has never run.
286 *
287 * Free resources of a fibril that has been created with fibril_create()
288 * but never readied using fibril_add_ready().
289 *
290 * @param fid Pointer to the fibril structure of the fibril to be
291 * added.
292 */
293void fibril_destroy(fid_t fid)
294{
295 fibril_t *fibril = (fibril_t *) fid;
296
297 as_area_destroy(fibril->stack);
298 fibril_teardown(fibril, false);
299}
300
301/** Add a fibril to the ready list.
302 *
303 * @param fid Pointer to the fibril structure of the fibril to be
304 * added.
305 *
306 */
307void fibril_add_ready(fid_t fid)
308{
309 fibril_t *fibril = (fibril_t *) fid;
310
311 futex_lock(&fibril_futex);
312 list_append(&fibril->link, &ready_list);
313 futex_unlock(&fibril_futex);
314}
315
316/** Add a fibril to the manager list.
317 *
318 * @param fid Pointer to the fibril structure of the fibril to be
319 * added.
320 *
321 */
322void fibril_add_manager(fid_t fid)
323{
324 fibril_t *fibril = (fibril_t *) fid;
325
326 futex_lock(&fibril_futex);
327 list_append(&fibril->link, &manager_list);
328 futex_unlock(&fibril_futex);
329}
330
331/** Remove one manager from the manager list. */
332void fibril_remove_manager(void)
333{
334 futex_lock(&fibril_futex);
335 if (!list_empty(&manager_list))
336 list_remove(list_first(&manager_list));
337 futex_unlock(&fibril_futex);
338}
339
340fibril_t *fibril_self(void)
341{
342 return __tcb_get()->fibril_data;
343}
344
345/** Return fibril id of the currently running fibril.
346 *
347 * @return fibril ID of the currently running fibril.
348 *
349 */
350fid_t fibril_get_id(void)
351{
352 return (fid_t) fibril_self();
353}
354
355void fibril_yield(void)
356{
357 fibril_switch(FIBRIL_PREEMPT);
358}
359
360/** @}
361 */
Note: See TracBrowser for help on using the repository browser.