source: mainline/uspace/lib/c/generic/fibril.c@ 95838f1

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

Switch async_futex to using futex_lock/unlock.

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