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

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

Introduce context_swap() and use it to simplify fibril implementation.

This wraps the use of context_save()/context_restore(), to provide a more
natural (and easier to understand) control flow. The patch also hides the
details of the underlying context, requiring the use of context_get_pc()
to acquire program counter. This might allow context_swap() to only store
one pointer in the context, storing the remaining information in its stack
frame. However, that is not done in this patch.

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