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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a8b0c5d was 3b1cc8d, checked in by GitHub <noreply@…>, 7 years ago

Add notification queue to the async framework. (#40)

Instead of running notification handlers in the same fibril that received it,
forcing us to allocate a new fibril when the handler blocks, we instead queue
the notifications, and allow an arbitrary but fixed number of dedicated fibrils
handle them.

Although a service can increase the number of handler fibrils to reduce latency,
there are now no dynamic allocations due to received notifications.
When the same notification is received again while the older instance is still
in queue, the new notification overwrites the old and increments a counter
of received notifications.

The counter is currently unused, because passing it to the handler would
require extensive changes to user code, but it should be straightforward
to make use of it should the need arise.

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