source: mainline/uspace/lib/c/generic/async/server.c@ 7137f74c

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

Replace a bunch of direct uses of futex_t.

  • Property mode set to 100644
File size: 44.9 KB
RevLine 
[06502f7d]1/*
[df4ed85]2 * Copyright (c) 2006 Ondrej Palkovsky
[06502f7d]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.
[b2951e2]27 */
28
[a46da63]29/** @addtogroup libc
[b2951e2]30 * @{
31 */
32/** @file
[c07544d3]33 */
[06502f7d]34
[80649a91]35/**
36 * Asynchronous library
37 *
[c07544d3]38 * The aim of this library is to provide a facility for writing programs which
39 * utilize the asynchronous nature of HelenOS IPC, yet using a normal way of
40 * programming.
[80649a91]41 *
[79ae36dd]42 * You should be able to write very simple multithreaded programs. The async
43 * framework will automatically take care of most of the synchronization
44 * problems.
[80649a91]45 *
[9591265]46 * Example of use (pseudo C):
[c07544d3]47 *
[80649a91]48 * 1) Multithreaded client application
[9591265]49 *
[c07544d3]50 * fibril_create(fibril1, ...);
51 * fibril_create(fibril2, ...);
52 * ...
53 *
54 * int fibril1(void *arg)
55 * {
[79ae36dd]56 * conn = async_connect_me_to(...);
57 *
58 * exch = async_exchange_begin(conn);
59 * c1 = async_send(exch);
60 * async_exchange_end(exch);
61 *
62 * exch = async_exchange_begin(conn);
63 * c2 = async_send(exch);
64 * async_exchange_end(exch);
65 *
[c07544d3]66 * async_wait_for(c1);
67 * async_wait_for(c2);
68 * ...
69 * }
[80649a91]70 *
71 *
72 * 2) Multithreaded server application
73 *
[c07544d3]74 * main()
75 * {
76 * async_manager();
77 * }
78 *
[984a9ba]79 * port_handler(ipc_call_t *icall)
[c07544d3]80 * {
81 * if (want_refuse) {
[984a9ba]82 * async_answer_0(icall, ELIMIT);
[c07544d3]83 * return;
84 * }
[80649a91]85 *
[984a9ba]86 * async_answer_0(icall, EOK);
[53ca318]87 *
[984a9ba]88 * async_get_call(&call);
89 * somehow_handle_the_call(&call);
90 * async_answer_2(&call, 1, 2, 3);
91 *
92 * async_get_call(&call);
[c07544d3]93 * ...
94 * }
[a2cd194]95 *
[80649a91]96 */
[9591265]97
[64d2b10]98#define LIBC_ASYNC_C_
99#include <ipc/ipc.h>
[80649a91]100#include <async.h>
[49a796f1]101#include "../private/async.h"
[64d2b10]102#undef LIBC_ASYNC_C_
103
[8820544]104#include <ipc/irq.h>
105#include <ipc/event.h>
[64d2b10]106#include <futex.h>
[bc1f1c2]107#include <fibril.h>
[d9c8c81]108#include <adt/hash_table.h>
[853802e]109#include <adt/hash.h>
[d9c8c81]110#include <adt/list.h>
[80649a91]111#include <assert.h>
112#include <errno.h>
[daa90e8]113#include <sys/time.h>
[c0699467]114#include <libarch/barrier.h>
[3e6a98c5]115#include <stdbool.h>
[38d150e]116#include <stdlib.h>
[79ae36dd]117#include <mem.h>
118#include <stdlib.h>
[e2ab36f1]119#include <macros.h>
[101516d]120#include <as.h>
[ae6021d]121#include <abi/mm/as.h>
[49a796f1]122#include "../private/libc.h"
[d73d992]123#include "../private/fibril.h"
[5da7199]124
[8dab988]125#define DPRINTF(...) ((void) 0)
126
[79ae36dd]127/** Async framework global futex */
[927a181e]128futex_t async_futex = FUTEX_INITIALIZER;
[80649a91]129
[8619f25]130/** Number of threads waiting for IPC in the kernel. */
[c8afd5a]131static atomic_t threads_in_ipc_wait = { 0 };
[8619f25]132
[79ae36dd]133/** Call data */
[80649a91]134typedef struct {
135 link_t link;
136 ipc_call_t call;
137} msg_t;
138
[79ae36dd]139/* Client connection data */
[c80fdd0]140typedef struct {
[062d900]141 ht_link_t link;
[a35b458]142
[649f087]143 task_id_t in_task_id;
[79ae36dd]144 atomic_t refcnt;
[c80fdd0]145 void *data;
146} client_t;
147
[79ae36dd]148/* Server connection data */
[80649a91]149typedef struct {
[49d072e]150 awaiter_t wdata;
[a35b458]151
[e70bfa5]152 /** Hash table link. */
[062d900]153 ht_link_t link;
[a35b458]154
[e2ab36f1]155 /** Incoming client task ID. */
156 task_id_t in_task_id;
[a35b458]157
[e70bfa5]158 /** Incoming phone hash. */
[96b02eb9]159 sysarg_t in_phone_hash;
[a35b458]160
[23882034]161 /** Link to the client tracking structure. */
162 client_t *client;
[a35b458]163
[e70bfa5]164 /** Messages that should be delivered to this fibril. */
[b72efe8]165 list_t msg_queue;
[a35b458]166
[e70bfa5]167 /** Call data of the opening call. */
[80649a91]168 ipc_call_t call;
[a35b458]169
[e70bfa5]170 /** Identification of the closing call. */
[eadaeae8]171 cap_call_handle_t close_chandle;
[a35b458]172
[e70bfa5]173 /** Fibril function that will be used to handle the connection. */
[b688fd8]174 async_port_handler_t handler;
[a35b458]175
[b688fd8]176 /** Client data */
177 void *data;
[80649a91]178} connection_t;
179
[8dab988]180/* Member of notification_t::msg_list. */
181typedef struct {
182 link_t link;
183 ipc_call_t calldata;
184} notification_msg_t;
185
[8820544]186/* Notification data */
187typedef struct {
[3b1cc8d]188 /** notification_hash_table link */
189 ht_link_t htlink;
190
191 /** notification_queue link */
192 link_t qlink;
[a35b458]193
[8820544]194 /** Notification method */
195 sysarg_t imethod;
[a35b458]196
[8820544]197 /** Notification handler */
198 async_notification_handler_t handler;
[a35b458]199
[3b1cc8d]200 /** Notification handler argument */
201 void *arg;
202
[8dab988]203 /** List of arrived notifications. */
204 list_t msg_list;
[8820544]205} notification_t;
206
[bc1f1c2]207/** Identifier of the incoming connection handled by the current fibril. */
[79ae36dd]208static fibril_local connection_t *fibril_connection;
[e70bfa5]209
[46eec3b]210static void *default_client_data_constructor(void)
211{
212 return NULL;
213}
214
215static void default_client_data_destructor(void *data)
216{
217}
218
219static async_client_data_ctor_t async_client_data_create =
220 default_client_data_constructor;
221static async_client_data_dtor_t async_client_data_destroy =
222 default_client_data_destructor;
223
224void async_set_client_data_constructor(async_client_data_ctor_t ctor)
225{
[f302586]226 assert(async_client_data_create == default_client_data_constructor);
[46eec3b]227 async_client_data_create = ctor;
228}
229
230void async_set_client_data_destructor(async_client_data_dtor_t dtor)
231{
[f302586]232 assert(async_client_data_destroy == default_client_data_destructor);
[46eec3b]233 async_client_data_destroy = dtor;
234}
235
[9bde0d5]236static FIBRIL_RMUTEX_INITIALIZE(client_mutex);
[c80fdd0]237static hash_table_t client_hash_table;
[3b1cc8d]238
239// TODO: lockfree notification_queue?
[9bde0d5]240static FIBRIL_RMUTEX_INITIALIZE(notification_mutex);
[8820544]241static hash_table_t notification_hash_table;
[3b1cc8d]242static LIST_INITIALIZE(notification_queue);
243static FIBRIL_SEMAPHORE_INITIALIZE(notification_semaphore, 0);
244
[8dab988]245static LIST_INITIALIZE(notification_freelist);
246static long notification_freelist_total = 0;
247static long notification_freelist_used = 0;
248
[8820544]249static sysarg_t notification_avail = 0;
250
[3bd1d7d4]251/* The remaining structures are guarded by async_futex. */
252static hash_table_t conn_hash_table;
253static LIST_INITIALIZE(timeout_list);
254
[8820544]255static size_t client_key_hash(void *key)
[c80fdd0]256{
[8820544]257 task_id_t in_task_id = *(task_id_t *) key;
258 return in_task_id;
[c80fdd0]259}
260
[062d900]261static size_t client_hash(const ht_link_t *item)
[c80fdd0]262{
[062d900]263 client_t *client = hash_table_get_inst(item, client_t, link);
264 return client_key_hash(&client->in_task_id);
[c80fdd0]265}
266
[8820544]267static bool client_key_equal(void *key, const ht_link_t *item)
[c80fdd0]268{
[8820544]269 task_id_t in_task_id = *(task_id_t *) key;
[062d900]270 client_t *client = hash_table_get_inst(item, client_t, link);
[8820544]271 return in_task_id == client->in_task_id;
[c80fdd0]272}
273
274/** Operations for the client hash table. */
[062d900]275static hash_table_ops_t client_hash_table_ops = {
[c80fdd0]276 .hash = client_hash,
[062d900]277 .key_hash = client_key_hash,
278 .key_equal = client_key_equal,
[4e00f87]279 .equal = NULL,
280 .remove_callback = NULL
[c80fdd0]281};
[80649a91]282
[853802e]283typedef struct {
284 task_id_t task_id;
285 sysarg_t phone_hash;
286} conn_key_t;
287
288/** Compute hash into the connection hash table
[e70bfa5]289 *
[853802e]290 * The hash is based on the source task ID and the source phone hash. The task
291 * ID is included in the hash because a phone hash alone might not be unique
292 * while we still track connections for killed tasks due to kernel's recycling
293 * of phone structures.
294 *
295 * @param key Pointer to the connection key structure.
[c07544d3]296 *
297 * @return Index into the connection hash table.
[e70bfa5]298 *
299 */
[062d900]300static size_t conn_key_hash(void *key)
[450cd3a]301{
[853802e]302 conn_key_t *ck = (conn_key_t *) key;
303
304 size_t hash = 0;
305 hash = hash_combine(hash, LOWER32(ck->task_id));
306 hash = hash_combine(hash, UPPER32(ck->task_id));
307 hash = hash_combine(hash, ck->phone_hash);
308 return hash;
[450cd3a]309}
[06502f7d]310
[062d900]311static size_t conn_hash(const ht_link_t *item)
[450cd3a]312{
[062d900]313 connection_t *conn = hash_table_get_inst(item, connection_t, link);
[853802e]314 return conn_key_hash(&(conn_key_t){
315 .task_id = conn->in_task_id,
316 .phone_hash = conn->in_phone_hash
317 });
[450cd3a]318}
[06502f7d]319
[062d900]320static bool conn_key_equal(void *key, const ht_link_t *item)
[450cd3a]321{
[853802e]322 conn_key_t *ck = (conn_key_t *) key;
[062d900]323 connection_t *conn = hash_table_get_inst(item, connection_t, link);
[853802e]324 return ((ck->task_id == conn->in_task_id) &&
325 (ck->phone_hash == conn->in_phone_hash));
[450cd3a]326}
327
[e70bfa5]328/** Operations for the connection hash table. */
[062d900]329static hash_table_ops_t conn_hash_table_ops = {
[80649a91]330 .hash = conn_hash,
[062d900]331 .key_hash = conn_key_hash,
332 .key_equal = conn_key_equal,
[4e00f87]333 .equal = NULL,
334 .remove_callback = NULL
[80649a91]335};
336
[9ef495f]337static client_t *async_client_get(task_id_t client_id, bool create)
338{
339 client_t *client = NULL;
[a35b458]340
[9bde0d5]341 fibril_rmutex_lock(&client_mutex);
[9ef495f]342 ht_link_t *link = hash_table_find(&client_hash_table, &client_id);
343 if (link) {
344 client = hash_table_get_inst(link, client_t, link);
345 atomic_inc(&client->refcnt);
346 } else if (create) {
[3bd1d7d4]347 // TODO: move the malloc out of critical section
[9bde0d5]348 /* malloc() is rmutex safe. */
[9ef495f]349 client = malloc(sizeof(client_t));
350 if (client) {
351 client->in_task_id = client_id;
352 client->data = async_client_data_create();
[a35b458]353
[9ef495f]354 atomic_set(&client->refcnt, 1);
355 hash_table_insert(&client_hash_table, &client->link);
356 }
357 }
[a35b458]358
[9bde0d5]359 fibril_rmutex_unlock(&client_mutex);
[9ef495f]360 return client;
361}
362
363static void async_client_put(client_t *client)
364{
365 bool destroy;
[a35b458]366
[9bde0d5]367 fibril_rmutex_lock(&client_mutex);
[a35b458]368
[9ef495f]369 if (atomic_predec(&client->refcnt) == 0) {
370 hash_table_remove(&client_hash_table, &client->in_task_id);
371 destroy = true;
372 } else
373 destroy = false;
[a35b458]374
[9bde0d5]375 fibril_rmutex_unlock(&client_mutex);
[a35b458]376
[9ef495f]377 if (destroy) {
378 if (client->data)
379 async_client_data_destroy(client->data);
[a35b458]380
[9ef495f]381 free(client);
382 }
383}
384
385/** Wrapper for client connection fibril.
386 *
387 * When a new connection arrives, a fibril with this implementing
388 * function is created.
389 *
390 * @param arg Connection structure pointer.
391 *
392 * @return Always zero.
393 *
394 */
[b7fd2a0]395static errno_t connection_fibril(void *arg)
[9ef495f]396{
397 assert(arg);
[a35b458]398
[9ef495f]399 /*
400 * Setup fibril-local connection pointer.
401 */
402 fibril_connection = (connection_t *) arg;
[a35b458]403
[9ef495f]404 /*
405 * Add our reference for the current connection in the client task
406 * tracking structure. If this is the first reference, create and
407 * hash in a new tracking structure.
408 */
[a35b458]409
[9ef495f]410 client_t *client = async_client_get(fibril_connection->in_task_id, true);
411 if (!client) {
[061274f]412 ipc_answer_0(fibril_connection->call.cap_handle, ENOMEM);
[9ef495f]413 return 0;
414 }
[a35b458]415
[9ef495f]416 fibril_connection->client = client;
[a35b458]417
[9ef495f]418 /*
419 * Call the connection handler function.
420 */
[984a9ba]421 fibril_connection->handler(&fibril_connection->call,
422 fibril_connection->data);
[a35b458]423
[9ef495f]424 /*
425 * Remove the reference for this client task connection.
426 */
427 async_client_put(client);
[a35b458]428
[9ef495f]429 /*
430 * Remove myself from the connection hash table.
431 */
[95838f1]432 futex_lock(&async_futex);
[853802e]433 hash_table_remove(&conn_hash_table, &(conn_key_t){
434 .task_id = fibril_connection->in_task_id,
435 .phone_hash = fibril_connection->in_phone_hash
436 });
[95838f1]437 futex_unlock(&async_futex);
[a35b458]438
[9ef495f]439 /*
440 * Answer all remaining messages with EHANGUP.
441 */
442 while (!list_empty(&fibril_connection->msg_queue)) {
443 msg_t *msg =
444 list_get_instance(list_first(&fibril_connection->msg_queue),
445 msg_t, link);
[a35b458]446
[9ef495f]447 list_remove(&msg->link);
[061274f]448 ipc_answer_0(msg->call.cap_handle, EHANGUP);
[9ef495f]449 free(msg);
450 }
[a35b458]451
[9ef495f]452 /*
453 * If the connection was hung-up, answer the last call,
454 * i.e. IPC_M_PHONE_HUNGUP.
455 */
[01c3bb4]456 if (fibril_connection->close_chandle)
457 ipc_answer_0(fibril_connection->close_chandle, EOK);
[a35b458]458
[9ef495f]459 free(fibril_connection);
[d5c1051]460 return EOK;
[9ef495f]461}
462
463/** Create a new fibril for a new connection.
464 *
[01c3bb4]465 * Create new fibril for connection, fill in connection structures and insert it
466 * into the hash table, so that later we can easily do routing of messages to
467 * particular fibrils.
[9ef495f]468 *
[01c3bb4]469 * @param in_task_id Identification of the incoming connection.
470 * @param in_phone_hash Identification of the incoming connection.
[061274f]471 * @param call Call data of the opening call. If call is NULL,
472 * the connection was opened by accepting the
473 * IPC_M_CONNECT_TO_ME call and this function is
474 * called directly by the server.
[01c3bb4]475 * @param handler Connection handler.
476 * @param data Client argument to pass to the connection handler.
[9ef495f]477 *
[01c3bb4]478 * @return New fibril id or NULL on failure.
[9ef495f]479 *
480 */
481static fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
[061274f]482 ipc_call_t *call, async_port_handler_t handler, void *data)
[9ef495f]483{
484 connection_t *conn = malloc(sizeof(*conn));
485 if (!conn) {
[061274f]486 if (call)
487 ipc_answer_0(call->cap_handle, ENOMEM);
[a35b458]488
[9ef495f]489 return (uintptr_t) NULL;
490 }
[a35b458]491
[9ef495f]492 conn->in_task_id = in_task_id;
493 conn->in_phone_hash = in_phone_hash;
494 list_initialize(&conn->msg_queue);
[01c3bb4]495 conn->close_chandle = CAP_NIL;
[9ef495f]496 conn->handler = handler;
497 conn->data = data;
[a35b458]498
[9ef495f]499 if (call)
500 conn->call = *call;
[061274f]501 else
502 conn->call.cap_handle = CAP_NIL;
[a35b458]503
[9ef495f]504 /* We will activate the fibril ASAP */
505 conn->wdata.active = true;
506 conn->wdata.fid = fibril_create(connection_fibril, conn);
[a35b458]507
[9ef495f]508 if (conn->wdata.fid == 0) {
509 free(conn);
[a35b458]510
[061274f]511 if (call)
512 ipc_answer_0(call->cap_handle, ENOMEM);
[a35b458]513
[9ef495f]514 return (uintptr_t) NULL;
515 }
[a35b458]516
[9ef495f]517 /* Add connection to the connection hash table */
[a35b458]518
[95838f1]519 futex_lock(&async_futex);
[9ef495f]520 hash_table_insert(&conn_hash_table, &conn->link);
[95838f1]521 futex_unlock(&async_futex);
[a35b458]522
[9ef495f]523 fibril_add_ready(conn->wdata.fid);
[a35b458]524
[9ef495f]525 return conn->wdata.fid;
526}
527
[78bb04b]528/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
529 *
530 * Ask through phone for a new connection to some service.
531 *
532 * @param exch Exchange for sending the message.
533 * @param iface Callback interface.
534 * @param arg1 User defined argument.
535 * @param arg2 User defined argument.
536 * @param handler Callback handler.
537 * @param data Handler data.
538 * @param port_id ID of the newly created port.
539 *
[cde999a]540 * @return Zero on success or an error code.
[78bb04b]541 *
542 */
[b7fd2a0]543errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
[78bb04b]544 sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
545{
546 if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
547 return EINVAL;
[a35b458]548
[78bb04b]549 if (exch == NULL)
550 return ENOENT;
[a35b458]551
[78bb04b]552 ipc_call_t answer;
553 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
554 &answer);
[a35b458]555
[fda19b8]556 errno_t rc;
557 async_wait_for(req, &rc);
558 if (rc != EOK)
559 return rc;
[a35b458]560
[fda19b8]561 rc = async_create_port_internal(iface, handler, data, port_id);
562 if (rc != EOK)
563 return rc;
[a35b458]564
[fda19b8]565 sysarg_t phone_hash = IPC_GET_ARG5(answer);
[78bb04b]566 fid_t fid = async_new_connection(answer.in_task_id, phone_hash,
[061274f]567 NULL, handler, data);
[78bb04b]568 if (fid == (uintptr_t) NULL)
569 return ENOMEM;
[a35b458]570
[78bb04b]571 return EOK;
572}
573
[8820544]574static size_t notification_key_hash(void *key)
575{
576 sysarg_t id = *(sysarg_t *) key;
577 return id;
578}
579
580static size_t notification_hash(const ht_link_t *item)
581{
582 notification_t *notification =
[3b1cc8d]583 hash_table_get_inst(item, notification_t, htlink);
[8820544]584 return notification_key_hash(&notification->imethod);
585}
586
587static bool notification_key_equal(void *key, const ht_link_t *item)
588{
589 sysarg_t id = *(sysarg_t *) key;
590 notification_t *notification =
[3b1cc8d]591 hash_table_get_inst(item, notification_t, htlink);
[8820544]592 return id == notification->imethod;
593}
594
595/** Operations for the notification hash table. */
596static hash_table_ops_t notification_hash_table_ops = {
597 .hash = notification_hash,
598 .key_hash = notification_key_hash,
599 .key_equal = notification_key_equal,
600 .equal = NULL,
601 .remove_callback = NULL
602};
603
[e70bfa5]604/** Sort in current fibril's timeout request.
[49d072e]605 *
[c07544d3]606 * @param wd Wait data of the current fibril.
607 *
[49d072e]608 */
[b6ee5b1]609void async_insert_timeout(awaiter_t *wd)
[49d072e]610{
[79ae36dd]611 assert(wd);
[a35b458]612
[f53cc81]613 wd->to_event.occurred = false;
614 wd->to_event.inlist = true;
[a35b458]615
[b72efe8]616 link_t *tmp = timeout_list.head.next;
617 while (tmp != &timeout_list.head) {
[3bacee1]618 awaiter_t *cur =
619 list_get_instance(tmp, awaiter_t, to_event.link);
[a35b458]620
[f53cc81]621 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
[49d072e]622 break;
[a35b458]623
[49d072e]624 tmp = tmp->next;
625 }
[a35b458]626
[b72efe8]627 list_insert_before(&wd->to_event.link, tmp);
[49d072e]628}
629
[e70bfa5]630/** Try to route a call to an appropriate connection fibril.
[80649a91]631 *
[36c9234]632 * If the proper connection fibril is found, a message with the call is added to
633 * its message queue. If the fibril was not active, it is activated and all
634 * timeouts are unregistered.
635 *
[061274f]636 * @param call Data of the incoming call.
[c07544d3]637 *
638 * @return False if the call doesn't match any connection.
[47b7006]639 * @return True if the call was passed to the respective connection fibril.
[36c9234]640 *
[80649a91]641 */
[061274f]642static bool route_call(ipc_call_t *call)
[450cd3a]643{
[79ae36dd]644 assert(call);
[a35b458]645
[95838f1]646 futex_lock(&async_futex);
[a35b458]647
[853802e]648 ht_link_t *link = hash_table_find(&conn_hash_table, &(conn_key_t){
649 .task_id = call->in_task_id,
650 .phone_hash = call->in_phone_hash
651 });
[8820544]652 if (!link) {
[95838f1]653 futex_unlock(&async_futex);
[c07544d3]654 return false;
[450cd3a]655 }
[a35b458]656
[8820544]657 connection_t *conn = hash_table_get_inst(link, connection_t, link);
[a35b458]658
[c07544d3]659 msg_t *msg = malloc(sizeof(*msg));
660 if (!msg) {
[95838f1]661 futex_unlock(&async_futex);
[c07544d3]662 return false;
663 }
[a35b458]664
[80649a91]665 msg->call = *call;
666 list_append(&msg->link, &conn->msg_queue);
[a35b458]667
[228e490]668 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
[061274f]669 conn->close_chandle = call->cap_handle;
[a35b458]670
[36c9234]671 /* If the connection fibril is waiting for an event, activate it */
[49d072e]672 if (!conn->wdata.active) {
[a35b458]673
[49d072e]674 /* If in timeout list, remove it */
[f53cc81]675 if (conn->wdata.to_event.inlist) {
676 conn->wdata.to_event.inlist = false;
677 list_remove(&conn->wdata.to_event.link);
[49d072e]678 }
[a35b458]679
[c07544d3]680 conn->wdata.active = true;
[bc1f1c2]681 fibril_add_ready(conn->wdata.fid);
[80649a91]682 }
[a35b458]683
[95838f1]684 futex_unlock(&async_futex);
[c07544d3]685 return true;
686}
[80649a91]687
[3b1cc8d]688/** Function implementing the notification handler fibril. Never returns. */
689static errno_t notification_fibril_func(void *arg)
690{
691 (void) arg;
692
693 while (true) {
694 fibril_semaphore_down(&notification_semaphore);
695
[9bde0d5]696 fibril_rmutex_lock(&notification_mutex);
[3b1cc8d]697
698 /*
699 * The semaphore ensures that if we get this far,
700 * the queue must be non-empty.
701 */
702 assert(!list_empty(&notification_queue));
703
704 notification_t *notification = list_get_instance(
705 list_first(&notification_queue), notification_t, qlink);
706
707 async_notification_handler_t handler = notification->handler;
708 void *arg = notification->arg;
709
[8dab988]710 notification_msg_t *m = list_pop(&notification->msg_list,
711 notification_msg_t, link);
712 assert(m);
713 ipc_call_t calldata = m->calldata;
714
715 notification_freelist_used--;
716
717 if (notification_freelist_total > 64 &&
718 notification_freelist_total > 2 * notification_freelist_used) {
719 /* Going to free the structure if we have too much. */
720 notification_freelist_total--;
721 } else {
722 /* Otherwise add to freelist. */
723 list_append(&m->link, &notification_freelist);
724 m = NULL;
725 }
[3b1cc8d]726
[8dab988]727 if (list_empty(&notification->msg_list))
728 list_remove(&notification->qlink);
[3b1cc8d]729
[9bde0d5]730 fibril_rmutex_unlock(&notification_mutex);
[3b1cc8d]731
732 if (handler)
733 handler(&calldata, arg);
[8dab988]734
735 free(m);
[3b1cc8d]736 }
737
738 /* Not reached. */
739 return EOK;
740}
741
742/**
743 * Creates a new dedicated fibril for handling notifications.
744 * By default, there is one such fibril. This function can be used to
745 * create more in order to increase the number of notification that can
746 * be processed concurrently.
747 *
748 * Currently, there is no way to destroy those fibrils after they are created.
749 */
750errno_t async_spawn_notification_handler(void)
751{
752 fid_t f = fibril_create(notification_fibril_func, NULL);
753 if (f == 0)
754 return ENOMEM;
755
756 fibril_add_ready(f);
757 return EOK;
758}
759
760/** Queue notification.
[c07544d3]761 *
[c170438]762 * @param call Data of the incoming call.
[58563585]763 *
[c07544d3]764 */
[3b1cc8d]765static void queue_notification(ipc_call_t *call)
[c07544d3]766{
[c170438]767 assert(call);
[a35b458]768
[9bde0d5]769 fibril_rmutex_lock(&notification_mutex);
[a35b458]770
[8dab988]771 notification_msg_t *m = list_pop(&notification_freelist,
772 notification_msg_t, link);
773
774 if (!m) {
[9bde0d5]775 fibril_rmutex_unlock(&notification_mutex);
[8dab988]776 m = malloc(sizeof(notification_msg_t));
777 if (!m) {
778 DPRINTF("Out of memory.\n");
779 abort();
780 }
781
[9bde0d5]782 fibril_rmutex_lock(&notification_mutex);
[8dab988]783 notification_freelist_total++;
784 }
785
[8820544]786 ht_link_t *link = hash_table_find(&notification_hash_table,
[c170438]787 &IPC_GET_IMETHOD(*call));
[3b1cc8d]788 if (!link) {
789 /* Invalid notification. */
790 // TODO: Make sure this can't happen and turn it into assert.
[8dab988]791 notification_freelist_total--;
[9bde0d5]792 fibril_rmutex_unlock(&notification_mutex);
[8dab988]793 free(m);
[3b1cc8d]794 return;
[8820544]795 }
[a35b458]796
[3b1cc8d]797 notification_t *notification =
798 hash_table_get_inst(link, notification_t, htlink);
799
[8dab988]800 notification_freelist_used++;
801 m->calldata = *call;
802 list_append(&m->link, &notification->msg_list);
[3b1cc8d]803
[8dab988]804 if (!link_in_use(&notification->qlink))
805 list_append(&notification->qlink, &notification_queue);
[3b1cc8d]806
[9bde0d5]807 fibril_rmutex_unlock(&notification_mutex);
[3b1cc8d]808
809 fibril_semaphore_up(&notification_semaphore);
810}
811
812/**
813 * Creates a new notification structure and inserts it into the hash table.
814 *
815 * @param handler Function to call when notification is received.
816 * @param arg Argument for the handler function.
817 * @return The newly created notification structure.
818 */
819static notification_t *notification_create(async_notification_handler_t handler, void *arg)
820{
821 notification_t *notification = calloc(1, sizeof(notification_t));
822 if (!notification)
823 return NULL;
824
825 notification->handler = handler;
826 notification->arg = arg;
827
[8dab988]828 list_initialize(&notification->msg_list);
829
[3b1cc8d]830 fid_t fib = 0;
831
[9bde0d5]832 fibril_rmutex_lock(&notification_mutex);
[3b1cc8d]833
834 if (notification_avail == 0) {
835 /* Attempt to create the first handler fibril. */
836 fib = fibril_create(notification_fibril_func, NULL);
837 if (fib == 0) {
[9bde0d5]838 fibril_rmutex_unlock(&notification_mutex);
[3b1cc8d]839 free(notification);
840 return NULL;
841 }
842 }
843
844 sysarg_t imethod = notification_avail;
845 notification_avail++;
846
847 notification->imethod = imethod;
848 hash_table_insert(&notification_hash_table, &notification->htlink);
849
[9bde0d5]850 fibril_rmutex_unlock(&notification_mutex);
[3b1cc8d]851
852 if (imethod == 0) {
853 assert(fib);
854 fibril_add_ready(fib);
855 }
[a35b458]856
[3b1cc8d]857 return notification;
[80649a91]858}
859
[8820544]860/** Subscribe to IRQ notification.
861 *
862 * @param inr IRQ number.
863 * @param handler Notification handler.
864 * @param data Notification handler client data.
865 * @param ucode Top-half pseudocode handler.
866 *
[071a1ddb]867 * @param[out] handle IRQ capability handle on success.
868 *
[cde999a]869 * @return An error code.
[8820544]870 *
871 */
[b7fd2a0]872errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
[eadaeae8]873 void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
[8820544]874{
[3b1cc8d]875 notification_t *notification = notification_create(handler, data);
[8820544]876 if (!notification)
877 return ENOMEM;
[a35b458]878
[eadaeae8]879 cap_irq_handle_t ihandle;
[3b1cc8d]880 errno_t rc = ipc_irq_subscribe(inr, notification->imethod, ucode,
881 &ihandle);
[071a1ddb]882 if (rc == EOK && handle != NULL) {
[eadaeae8]883 *handle = ihandle;
[9233e9d]884 }
[071a1ddb]885 return rc;
[8820544]886}
887
888/** Unsubscribe from IRQ notification.
889 *
[eadaeae8]890 * @param handle IRQ capability handle.
[8820544]891 *
[cde999a]892 * @return Zero on success or an error code.
[8820544]893 *
894 */
[eadaeae8]895errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
[8820544]896{
897 // TODO: Remove entry from hash table
898 // to avoid memory leak
[a35b458]899
[eadaeae8]900 return ipc_irq_unsubscribe(ihandle);
[8820544]901}
902
903/** Subscribe to event notifications.
904 *
905 * @param evno Event type to subscribe.
906 * @param handler Notification handler.
907 * @param data Notification handler client data.
908 *
[cde999a]909 * @return Zero on success or an error code.
[8820544]910 *
911 */
[b7fd2a0]912errno_t async_event_subscribe(event_type_t evno,
[8820544]913 async_notification_handler_t handler, void *data)
914{
[3b1cc8d]915 notification_t *notification = notification_create(handler, data);
[8820544]916 if (!notification)
917 return ENOMEM;
[a35b458]918
[3b1cc8d]919 return ipc_event_subscribe(evno, notification->imethod);
[8820544]920}
921
922/** Subscribe to task event notifications.
923 *
924 * @param evno Event type to subscribe.
925 * @param handler Notification handler.
926 * @param data Notification handler client data.
927 *
[cde999a]928 * @return Zero on success or an error code.
[8820544]929 *
930 */
[b7fd2a0]931errno_t async_event_task_subscribe(event_task_type_t evno,
[8820544]932 async_notification_handler_t handler, void *data)
933{
[3b1cc8d]934 notification_t *notification = notification_create(handler, data);
[8820544]935 if (!notification)
936 return ENOMEM;
[a35b458]937
[3b1cc8d]938 return ipc_event_task_subscribe(evno, notification->imethod);
[8820544]939}
940
941/** Unmask event notifications.
942 *
943 * @param evno Event type to unmask.
944 *
945 * @return Value returned by the kernel.
946 *
947 */
[b7fd2a0]948errno_t async_event_unmask(event_type_t evno)
[8820544]949{
950 return ipc_event_unmask(evno);
951}
952
953/** Unmask task event notifications.
954 *
955 * @param evno Event type to unmask.
956 *
957 * @return Value returned by the kernel.
958 *
959 */
[b7fd2a0]960errno_t async_event_task_unmask(event_task_type_t evno)
[8820544]961{
962 return ipc_event_task_unmask(evno);
963}
964
[e70bfa5]965/** Return new incoming message for the current (fibril-local) connection.
966 *
[984a9ba]967 * @param call Storage where the incoming call data will be stored.
968 * @param usecs Timeout in microseconds. Zero denotes no timeout.
969 *
970 * @return If no timeout was specified, then true is returned.
971 * @return If a timeout is specified, then true is returned unless
972 * the timeout expires prior to receiving a message.
[e70bfa5]973 *
974 */
[984a9ba]975bool async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
[80649a91]976{
[79ae36dd]977 assert(call);
978 assert(fibril_connection);
[a35b458]979
[7c3fb9b]980 /*
981 * Why doing this?
[79ae36dd]982 * GCC 4.1.0 coughs on fibril_connection-> dereference.
[6c46350]983 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
[c07544d3]984 * I would never expect to find so many errors in
985 * a compiler.
[6c46350]986 */
[79ae36dd]987 connection_t *conn = fibril_connection;
[a35b458]988
[95838f1]989 futex_lock(&async_futex);
[a35b458]990
[49d072e]991 if (usecs) {
[45cbcaf4]992 getuptime(&conn->wdata.to_event.expires);
[7f9d97f3]993 tv_add_diff(&conn->wdata.to_event.expires, usecs);
[c07544d3]994 } else
[f53cc81]995 conn->wdata.to_event.inlist = false;
[a35b458]996
[e70bfa5]997 /* If nothing in queue, wait until something arrives */
[6c46350]998 while (list_empty(&conn->msg_queue)) {
[01c3bb4]999 if (conn->close_chandle) {
[8c8f8d6]1000 /*
1001 * Handle the case when the connection was already
1002 * closed by the client but the server did not notice
1003 * the first IPC_M_PHONE_HUNGUP call and continues to
1004 * call async_get_call_timeout(). Repeat
[47b7006]1005 * IPC_M_PHONE_HUNGUP until the caller notices.
[8c8f8d6]1006 */
1007 memset(call, 0, sizeof(ipc_call_t));
[228e490]1008 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
[95838f1]1009 futex_unlock(&async_futex);
[984a9ba]1010 return true;
[8c8f8d6]1011 }
[a35b458]1012
[085bd54]1013 if (usecs)
[b6ee5b1]1014 async_insert_timeout(&conn->wdata);
[a35b458]1015
[c07544d3]1016 conn->wdata.active = false;
[a35b458]1017
[c7509e5]1018 /*
1019 * Note: the current fibril will be rescheduled either due to a
1020 * timeout or due to an arriving message destined to it. In the
1021 * former case, handle_expired_timeouts() and, in the latter
1022 * case, route_call() will perform the wakeup.
1023 */
[ab6edb6]1024 fibril_switch(FIBRIL_FROM_BLOCKED);
[a35b458]1025
[3bacee1]1026 if ((usecs) && (conn->wdata.to_event.occurred) &&
1027 (list_empty(&conn->msg_queue))) {
[e70bfa5]1028 /* If we timed out -> exit */
[95838f1]1029 futex_unlock(&async_futex);
[984a9ba]1030 return false;
[49d072e]1031 }
[450cd3a]1032 }
[a35b458]1033
[57dea62]1034 msg_t *msg = list_get_instance(list_first(&conn->msg_queue),
1035 msg_t, link);
[80649a91]1036 list_remove(&msg->link);
[a35b458]1037
[80649a91]1038 *call = msg->call;
1039 free(msg);
[a35b458]1040
[95838f1]1041 futex_unlock(&async_futex);
[984a9ba]1042 return true;
[80649a91]1043}
1044
[455f190]1045void *async_get_client_data(void)
1046{
1047 assert(fibril_connection);
1048 return fibril_connection->client->data;
1049}
1050
[e2ab36f1]1051void *async_get_client_data_by_id(task_id_t client_id)
[455f190]1052{
[e2ab36f1]1053 client_t *client = async_client_get(client_id, false);
[455f190]1054 if (!client)
1055 return NULL;
[a35b458]1056
[455f190]1057 if (!client->data) {
1058 async_client_put(client);
1059 return NULL;
1060 }
[a35b458]1061
[455f190]1062 return client->data;
1063}
1064
[e2ab36f1]1065void async_put_client_data_by_id(task_id_t client_id)
[455f190]1066{
[e2ab36f1]1067 client_t *client = async_client_get(client_id, false);
[a35b458]1068
[455f190]1069 assert(client);
1070 assert(client->data);
[a35b458]1071
[cdc8ee2d]1072 /* Drop the reference we got in async_get_client_data_by_hash(). */
1073 async_client_put(client);
[a35b458]1074
[cdc8ee2d]1075 /* Drop our own reference we got at the beginning of this function. */
[455f190]1076 async_client_put(client);
1077}
1078
[36c9234]1079/** Handle a call that was received.
1080 *
1081 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
1082 * Otherwise the call is routed to its connection fibril.
1083 *
[061274f]1084 * @param call Data of the incoming call.
[6b21292]1085 *
[36c9234]1086 */
[061274f]1087static void handle_call(ipc_call_t *call)
[80649a91]1088{
[79ae36dd]1089 assert(call);
[a35b458]1090
[d054ad3]1091 if (call->flags & IPC_CALL_ANSWERED) {
1092 /* Answer to a call made by us. */
1093 async_reply_received(call);
[e768aea]1094 return;
[d054ad3]1095 }
[e768aea]1096
[061274f]1097 if (call->cap_handle == CAP_NIL) {
[e768aea]1098 if (call->flags & IPC_CALL_NOTIF) {
1099 /* Kernel notification */
1100 queue_notification(call);
1101 }
[47b7006]1102 return;
[6b21292]1103 }
[a35b458]1104
[566992e1]1105 /* New connection */
1106 if (IPC_GET_IMETHOD(*call) == IPC_M_CONNECT_ME_TO) {
1107 iface_t iface = (iface_t) IPC_GET_ARG1(*call);
1108 sysarg_t in_phone_hash = IPC_GET_ARG5(*call);
[a35b458]1109
[49a796f1]1110 // TODO: Currently ignores all ports but the first one.
1111 void *data;
1112 async_port_handler_t handler =
1113 async_get_port_handler(iface, 0, &data);
[a35b458]1114
[061274f]1115 async_new_connection(call->in_task_id, in_phone_hash, call,
1116 handler, data);
[566992e1]1117 return;
1118 }
[a35b458]1119
[36c9234]1120 /* Try to route the call through the connection hash table */
[061274f]1121 if (route_call(call))
[47b7006]1122 return;
[a35b458]1123
[44c6d88d]1124 /* Unknown call from unknown phone - hang it up */
[061274f]1125 ipc_answer_0(call->cap_handle, EHANGUP);
[450cd3a]1126}
1127
[f2f0392]1128/** Fire all timeouts that expired. */
[e768aea]1129static suseconds_t handle_expired_timeouts(unsigned int *flags)
[c042bdd]1130{
[e768aea]1131 /* Make sure the async_futex is held. */
1132 futex_assert_is_locked(&async_futex);
1133
[c042bdd]1134 struct timeval tv;
[45cbcaf4]1135 getuptime(&tv);
[a35b458]1136
[e768aea]1137 bool fired = false;
[a35b458]1138
[b72efe8]1139 link_t *cur = list_first(&timeout_list);
1140 while (cur != NULL) {
[47b7006]1141 awaiter_t *waiter =
1142 list_get_instance(cur, awaiter_t, to_event.link);
[a35b458]1143
[e768aea]1144 if (tv_gt(&waiter->to_event.expires, &tv)) {
1145 if (fired) {
1146 *flags = SYNCH_FLAGS_NON_BLOCKING;
1147 return 0;
1148 }
1149 *flags = 0;
1150 return tv_sub_diff(&waiter->to_event.expires, &tv);
1151 }
[a35b458]1152
[f53cc81]1153 list_remove(&waiter->to_event.link);
1154 waiter->to_event.inlist = false;
1155 waiter->to_event.occurred = true;
[a35b458]1156
[36c9234]1157 /*
[c07544d3]1158 * Redundant condition?
1159 * The fibril should not be active when it gets here.
[c042bdd]1160 */
[49d072e]1161 if (!waiter->active) {
[c07544d3]1162 waiter->active = true;
[bc1f1c2]1163 fibril_add_ready(waiter->fid);
[e768aea]1164 fired = true;
[c042bdd]1165 }
[a35b458]1166
[b72efe8]1167 cur = list_first(&timeout_list);
[c042bdd]1168 }
[a35b458]1169
[e768aea]1170 if (fired) {
1171 *flags = SYNCH_FLAGS_NON_BLOCKING;
1172 return 0;
1173 }
1174
1175 return SYNCH_NO_TIMEOUT;
[c042bdd]1176}
1177
[36c9234]1178/** Endless loop dispatching incoming calls and answers.
1179 *
[c07544d3]1180 * @return Never returns.
1181 *
[36c9234]1182 */
[b7fd2a0]1183static errno_t async_manager_worker(void)
[80649a91]1184{
[c07544d3]1185 while (true) {
[95838f1]1186 futex_lock(&async_futex);
[ab6edb6]1187 fibril_switch(FIBRIL_FROM_MANAGER);
1188
1189 /*
1190 * The switch only returns when there is no non-manager fibril
1191 * it can run.
1192 */
[a35b458]1193
[1db6dfd]1194 unsigned int flags = SYNCH_FLAGS_NONE;
[e768aea]1195 suseconds_t next_timeout = handle_expired_timeouts(&flags);
1196 futex_unlock(&async_futex);
[a35b458]1197
[8619f25]1198 atomic_inc(&threads_in_ipc_wait);
[a35b458]1199
[c07544d3]1200 ipc_call_t call;
[d054ad3]1201 errno_t rc = ipc_wait(&call, next_timeout, flags);
[a35b458]1202
[8619f25]1203 atomic_dec(&threads_in_ipc_wait);
[a35b458]1204
[acf6b55]1205 if (rc == EOK)
1206 handle_call(&call);
[80649a91]1207 }
[01c3bb4]1208
[a46da63]1209 return 0;
[80649a91]1210}
1211
[36c9234]1212/** Function to start async_manager as a standalone fibril.
[c07544d3]1213 *
[36c9234]1214 * When more kernel threads are used, one async manager should exist per thread.
1215 *
[c07544d3]1216 * @param arg Unused.
1217 * @return Never returns.
[36c9234]1218 *
[a2cd194]1219 */
[b7fd2a0]1220static errno_t async_manager_fibril(void *arg)
[80649a91]1221{
[085bd54]1222 async_manager_worker();
[a46da63]1223 return 0;
[80649a91]1224}
[450cd3a]1225
[36c9234]1226/** Add one manager to manager list. */
[80649a91]1227void async_create_manager(void)
[450cd3a]1228{
[c170438]1229 fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
[86d7bfa]1230 if (fid != 0)
1231 fibril_add_manager(fid);
[80649a91]1232}
1233
1234/** Remove one manager from manager list */
1235void async_destroy_manager(void)
1236{
[bc1f1c2]1237 fibril_remove_manager();
[80649a91]1238}
1239
[36c9234]1240/** Initialize the async framework.
1241 *
1242 */
[49a796f1]1243void __async_server_init(void)
[80649a91]1244{
[062d900]1245 if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
[47b7006]1246 abort();
[a35b458]1247
[062d900]1248 if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
[47b7006]1249 abort();
[a35b458]1250
[8820544]1251 if (!hash_table_create(&notification_hash_table, 0, 0,
1252 &notification_hash_table_ops))
1253 abort();
[49a796f1]1254}
[a35b458]1255
[984a9ba]1256errno_t async_answer_0(ipc_call_t *call, errno_t retval)
[49a796f1]1257{
[984a9ba]1258 return ipc_answer_0(call->cap_handle, retval);
[450cd3a]1259}
[01ff41c]1260
[984a9ba]1261errno_t async_answer_1(ipc_call_t *call, errno_t retval, sysarg_t arg1)
[01ff41c]1262{
[984a9ba]1263 return ipc_answer_1(call->cap_handle, retval, arg1);
[49a796f1]1264}
[a35b458]1265
[984a9ba]1266errno_t async_answer_2(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1267 sysarg_t arg2)
1268{
[984a9ba]1269 return ipc_answer_2(call->cap_handle, retval, arg1, arg2);
[49a796f1]1270}
[a35b458]1271
[984a9ba]1272errno_t async_answer_3(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1273 sysarg_t arg2, sysarg_t arg3)
1274{
[984a9ba]1275 return ipc_answer_3(call->cap_handle, retval, arg1, arg2, arg3);
[49a796f1]1276}
[a35b458]1277
[984a9ba]1278errno_t async_answer_4(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1279 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1280{
[984a9ba]1281 return ipc_answer_4(call->cap_handle, retval, arg1, arg2, arg3, arg4);
[49a796f1]1282}
[a35b458]1283
[984a9ba]1284errno_t async_answer_5(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1285 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
1286{
[984a9ba]1287 return ipc_answer_5(call->cap_handle, retval, arg1, arg2, arg3, arg4,
1288 arg5);
[49a796f1]1289}
[a35b458]1290
[984a9ba]1291errno_t async_forward_fast(ipc_call_t *call, async_exch_t *exch,
[49a796f1]1292 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
1293{
[984a9ba]1294 assert(call);
1295
[49a796f1]1296 if (exch == NULL)
1297 return ENOENT;
[a35b458]1298
[984a9ba]1299 return ipc_forward_fast(call->cap_handle, exch->phone, imethod, arg1,
1300 arg2, mode);
[49a796f1]1301}
[a35b458]1302
[984a9ba]1303errno_t async_forward_slow(ipc_call_t *call, async_exch_t *exch,
[49a796f1]1304 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
1305 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
1306{
[984a9ba]1307 assert(call);
1308
[49a796f1]1309 if (exch == NULL)
1310 return ENOENT;
[a35b458]1311
[984a9ba]1312 return ipc_forward_slow(call->cap_handle, exch->phone, imethod, arg1,
1313 arg2, arg3, arg4, arg5, mode);
[01ff41c]1314}
1315
[49a796f1]1316/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
[36c9234]1317 *
[49a796f1]1318 * Ask through phone for a new connection to some service.
[01ff41c]1319 *
[9b1baac]1320 * @param exch Exchange for sending the message.
1321 * @param iface Callback interface.
1322 * @param arg2 User defined argument.
1323 * @param arg3 User defined argument.
[c07544d3]1324 *
[49a796f1]1325 * @return Zero on success or an error code.
[36c9234]1326 *
[01ff41c]1327 */
[9b1baac]1328errno_t async_connect_to_me(async_exch_t *exch, iface_t iface, sysarg_t arg2,
[49a796f1]1329 sysarg_t arg3)
[01ff41c]1330{
[79ae36dd]1331 if (exch == NULL)
[49a796f1]1332 return ENOENT;
[a35b458]1333
[49a796f1]1334 ipc_call_t answer;
[9b1baac]1335 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg2, arg3,
[49a796f1]1336 &answer);
[a35b458]1337
[49a796f1]1338 errno_t rc;
1339 async_wait_for(req, &rc);
1340 if (rc != EOK)
1341 return (errno_t) rc;
[a35b458]1342
[49a796f1]1343 return EOK;
1344}
[a35b458]1345
[49a796f1]1346/** Interrupt one thread of this task from waiting for IPC. */
1347void async_poke(void)
1348{
[c8afd5a]1349 if (atomic_get(&threads_in_ipc_wait) > 0)
1350 ipc_poke();
[01ff41c]1351}
1352
[49a796f1]1353/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
[0da4e41]1354 *
[47b7006]1355 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN
1356 * calls so that the user doesn't have to remember the meaning of each IPC
1357 * argument.
[0da4e41]1358 *
1359 * So far, this wrapper is to be used from within a connection fibril.
1360 *
[984a9ba]1361 * @param call Storage for the data of the IPC_M_SHARE_IN call.
1362 * @param size Destination address space area size.
[47b7006]1363 *
1364 * @return True on success, false on failure.
[0da4e41]1365 *
1366 */
[984a9ba]1367bool async_share_in_receive(ipc_call_t *call, size_t *size)
[0da4e41]1368{
[984a9ba]1369 assert(call);
[0da4e41]1370 assert(size);
[a35b458]1371
[984a9ba]1372 async_get_call(call);
[a35b458]1373
[984a9ba]1374 if (IPC_GET_IMETHOD(*call) != IPC_M_SHARE_IN)
[47b7006]1375 return false;
[a35b458]1376
[984a9ba]1377 *size = (size_t) IPC_GET_ARG1(*call);
[47b7006]1378 return true;
[0da4e41]1379}
1380
1381/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
1382 *
[fbcdeb8]1383 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
[47b7006]1384 * calls so that the user doesn't have to remember the meaning of each IPC
1385 * argument.
[0da4e41]1386 *
[984a9ba]1387 * @param call IPC_M_DATA_READ call to answer.
1388 * @param src Source address space base.
1389 * @param flags Flags to be used for sharing. Bits can be only cleared.
[47b7006]1390 *
1391 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1392 *
1393 */
[984a9ba]1394errno_t async_share_in_finalize(ipc_call_t *call, void *src, unsigned int flags)
[0da4e41]1395{
[984a9ba]1396 assert(call);
1397
[a69d42e]1398 // FIXME: The source has no business deciding destination address.
[984a9ba]1399 return ipc_answer_3(call->cap_handle, EOK, (sysarg_t) src, (sysarg_t) flags,
[2eadda9]1400 (sysarg_t) __progsymbols.end);
[0da4e41]1401}
1402
1403/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
1404 *
[47b7006]1405 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT
1406 * calls so that the user doesn't have to remember the meaning of each IPC
1407 * argument.
[0da4e41]1408 *
1409 * So far, this wrapper is to be used from within a connection fibril.
1410 *
[984a9ba]1411 * @param call Storage for the data of the IPC_M_SHARE_OUT call.
1412 * @param size Storage for the source address space area size.
1413 * @param flags Storage for the sharing flags.
[47b7006]1414 *
1415 * @return True on success, false on failure.
[0da4e41]1416 *
1417 */
[984a9ba]1418bool async_share_out_receive(ipc_call_t *call, size_t *size,
[01c3bb4]1419 unsigned int *flags)
[0da4e41]1420{
[984a9ba]1421 assert(call);
[0da4e41]1422 assert(size);
1423 assert(flags);
[a35b458]1424
[984a9ba]1425 async_get_call(call);
[a35b458]1426
[984a9ba]1427 if (IPC_GET_IMETHOD(*call) != IPC_M_SHARE_OUT)
[47b7006]1428 return false;
[a35b458]1429
[984a9ba]1430 *size = (size_t) IPC_GET_ARG2(*call);
1431 *flags = (unsigned int) IPC_GET_ARG3(*call);
[47b7006]1432 return true;
[0da4e41]1433}
1434
1435/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
1436 *
[47b7006]1437 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
1438 * calls so that the user doesn't have to remember the meaning of each IPC
1439 * argument.
[0da4e41]1440 *
[984a9ba]1441 * @param call IPC_M_DATA_WRITE call to answer.
1442 * @param dst Address of the storage for the destination address space area
1443 * base address.
[47b7006]1444 *
[01c3bb4]1445 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1446 *
1447 */
[984a9ba]1448errno_t async_share_out_finalize(ipc_call_t *call, void **dst)
[0da4e41]1449{
[984a9ba]1450 assert(call);
[0da4e41]1451
[2eadda9]1452 return ipc_answer_2(call->cap_handle, EOK, (sysarg_t) __progsymbols.end,
[984a9ba]1453 (sysarg_t) dst);
[d768d4c8]1454}
1455
1456/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1457 *
1458 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
1459 * calls so that the user doesn't have to remember the meaning of each IPC
1460 * argument.
1461 *
1462 * So far, this wrapper is to be used from within a connection fibril.
1463 *
[984a9ba]1464 * @param call Storage for the data of the IPC_M_DATA_READ.
1465 * @param size Storage for the maximum size. Can be NULL.
[d768d4c8]1466 *
1467 * @return True on success, false on failure.
1468 *
1469 */
[984a9ba]1470bool async_data_read_receive(ipc_call_t *call, size_t *size)
[0da4e41]1471{
[984a9ba]1472 assert(call);
[a35b458]1473
[984a9ba]1474 async_get_call(call);
[a35b458]1475
[984a9ba]1476 if (IPC_GET_IMETHOD(*call) != IPC_M_DATA_READ)
[47b7006]1477 return false;
[a35b458]1478
[0da4e41]1479 if (size)
[984a9ba]1480 *size = (size_t) IPC_GET_ARG2(*call);
[a35b458]1481
[47b7006]1482 return true;
[0da4e41]1483}
1484
1485/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
1486 *
[47b7006]1487 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
1488 * calls so that the user doesn't have to remember the meaning of each IPC
1489 * argument.
[0da4e41]1490 *
[984a9ba]1491 * @param call IPC_M_DATA_READ call to answer.
1492 * @param src Source address for the IPC_M_DATA_READ call.
1493 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
1494 * the maximum size announced by the sender.
[47b7006]1495 *
[01c3bb4]1496 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1497 *
1498 */
[984a9ba]1499errno_t async_data_read_finalize(ipc_call_t *call, const void *src, size_t size)
[0da4e41]1500{
[984a9ba]1501 assert(call);
1502
1503 return ipc_answer_2(call->cap_handle, EOK, (sysarg_t) src,
1504 (sysarg_t) size);
[0da4e41]1505}
1506
[b4cbef1]1507/** Wrapper for forwarding any read request
1508 *
1509 */
[b7fd2a0]1510errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
[79ae36dd]1511 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1512 ipc_call_t *dataptr)
[b4cbef1]1513{
[79ae36dd]1514 if (exch == NULL)
1515 return ENOENT;
[a35b458]1516
[984a9ba]1517 ipc_call_t call;
1518 if (!async_data_read_receive(&call, NULL)) {
1519 async_answer_0(&call, EINVAL);
[b4cbef1]1520 return EINVAL;
1521 }
[a35b458]1522
[79ae36dd]1523 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]1524 dataptr);
1525 if (msg == 0) {
[984a9ba]1526 async_answer_0(&call, EINVAL);
[b4cbef1]1527 return EINVAL;
1528 }
[a35b458]1529
[984a9ba]1530 errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
[b4cbef1]1531 IPC_FF_ROUTE_FROM_ME);
1532 if (retval != EOK) {
[ab9f443]1533 async_forget(msg);
[984a9ba]1534 async_answer_0(&call, retval);
[b4cbef1]1535 return retval;
1536 }
[a35b458]1537
[b7fd2a0]1538 errno_t rc;
[b4cbef1]1539 async_wait_for(msg, &rc);
[a35b458]1540
[b7fd2a0]1541 return (errno_t) rc;
[b4cbef1]1542}
1543
[0da4e41]1544/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1545 *
[47b7006]1546 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
1547 * calls so that the user doesn't have to remember the meaning of each IPC
1548 * argument.
[0da4e41]1549 *
1550 * So far, this wrapper is to be used from within a connection fibril.
1551 *
[984a9ba]1552 * @param call Storage for the data of the IPC_M_DATA_WRITE.
1553 * @param size Storage for the suggested size. May be NULL.
[5ae1c51]1554 *
1555 * @return True on success, false on failure.
1556 *
1557 */
[984a9ba]1558bool async_data_write_receive(ipc_call_t *call, size_t *size)
[0da4e41]1559{
[984a9ba]1560 assert(call);
[a35b458]1561
[984a9ba]1562 async_get_call(call);
[a35b458]1563
[984a9ba]1564 if (IPC_GET_IMETHOD(*call) != IPC_M_DATA_WRITE)
[47b7006]1565 return false;
[a35b458]1566
[0da4e41]1567 if (size)
[984a9ba]1568 *size = (size_t) IPC_GET_ARG2(*call);
[a35b458]1569
[47b7006]1570 return true;
[0da4e41]1571}
1572
1573/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
1574 *
[47b7006]1575 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
1576 * calls so that the user doesn't have to remember the meaning of each IPC
1577 * argument.
[0da4e41]1578 *
[984a9ba]1579 * @param call IPC_M_DATA_WRITE call to answer.
1580 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
1581 * @param size Final size for the IPC_M_DATA_WRITE call.
[b4cbef1]1582 *
[01c3bb4]1583 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1584 *
1585 */
[984a9ba]1586errno_t async_data_write_finalize(ipc_call_t *call, void *dst, size_t size)
[0da4e41]1587{
[984a9ba]1588 assert(call);
1589
1590 return async_answer_2(call, EOK, (sysarg_t) dst, (sysarg_t) size);
[0da4e41]1591}
1592
[eda925a]1593/** Wrapper for receiving binary data or strings
[8aa42e3]1594 *
1595 * This wrapper only makes it more comfortable to use async_data_write_*
[eda925a]1596 * functions to receive binary data or strings.
[8aa42e3]1597 *
[472c09d]1598 * @param data Pointer to data pointer (which should be later disposed
1599 * by free()). If the operation fails, the pointer is not
1600 * touched.
[eda925a]1601 * @param nullterm If true then the received data is always zero terminated.
1602 * This also causes to allocate one extra byte beyond the
1603 * raw transmitted data.
[b4cbef1]1604 * @param min_size Minimum size (in bytes) of the data to receive.
[472c09d]1605 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
1606 * no limit.
[eda925a]1607 * @param granulariy If non-zero then the size of the received data has to
[472c09d]1608 * be divisible by this value.
1609 * @param received If not NULL, the size of the received data is stored here.
[8aa42e3]1610 *
1611 * @return Zero on success or a value from @ref errno.h on failure.
1612 *
1613 */
[b7fd2a0]1614errno_t async_data_write_accept(void **data, const bool nullterm,
[eda925a]1615 const size_t min_size, const size_t max_size, const size_t granularity,
1616 size_t *received)
[8aa42e3]1617{
[79ae36dd]1618 assert(data);
[a35b458]1619
[984a9ba]1620 ipc_call_t call;
[8aa42e3]1621 size_t size;
[984a9ba]1622 if (!async_data_write_receive(&call, &size)) {
1623 async_answer_0(&call, EINVAL);
[8aa42e3]1624 return EINVAL;
1625 }
[a35b458]1626
[b4cbef1]1627 if (size < min_size) {
[984a9ba]1628 async_answer_0(&call, EINVAL);
[b4cbef1]1629 return EINVAL;
1630 }
[a35b458]1631
[8aa42e3]1632 if ((max_size > 0) && (size > max_size)) {
[984a9ba]1633 async_answer_0(&call, EINVAL);
[8aa42e3]1634 return EINVAL;
1635 }
[a35b458]1636
[472c09d]1637 if ((granularity > 0) && ((size % granularity) != 0)) {
[984a9ba]1638 async_answer_0(&call, EINVAL);
[472c09d]1639 return EINVAL;
1640 }
[a35b458]1641
[57dea62]1642 void *arg_data;
[a35b458]1643
[eda925a]1644 if (nullterm)
[57dea62]1645 arg_data = malloc(size + 1);
[eda925a]1646 else
[57dea62]1647 arg_data = malloc(size);
[a35b458]1648
[57dea62]1649 if (arg_data == NULL) {
[984a9ba]1650 async_answer_0(&call, ENOMEM);
[8aa42e3]1651 return ENOMEM;
1652 }
[a35b458]1653
[984a9ba]1654 errno_t rc = async_data_write_finalize(&call, arg_data, size);
[8aa42e3]1655 if (rc != EOK) {
[57dea62]1656 free(arg_data);
[8aa42e3]1657 return rc;
1658 }
[a35b458]1659
[eda925a]1660 if (nullterm)
[57dea62]1661 ((char *) arg_data)[size] = 0;
[a35b458]1662
[57dea62]1663 *data = arg_data;
[472c09d]1664 if (received != NULL)
1665 *received = size;
[a35b458]1666
[8aa42e3]1667 return EOK;
1668}
1669
[b4cbef1]1670/** Wrapper for voiding any data that is about to be received
1671 *
1672 * This wrapper can be used to void any pending data
1673 *
1674 * @param retval Error value from @ref errno.h to be returned to the caller.
1675 *
1676 */
[b7fd2a0]1677void async_data_write_void(errno_t retval)
[b4cbef1]1678{
[984a9ba]1679 ipc_call_t call;
1680 async_data_write_receive(&call, NULL);
1681 async_answer_0(&call, retval);
[b4cbef1]1682}
1683
1684/** Wrapper for forwarding any data that is about to be received
1685 *
1686 */
[b7fd2a0]1687errno_t async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
[79ae36dd]1688 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1689 ipc_call_t *dataptr)
[b4cbef1]1690{
[79ae36dd]1691 if (exch == NULL)
1692 return ENOENT;
[a35b458]1693
[984a9ba]1694 ipc_call_t call;
1695 if (!async_data_write_receive(&call, NULL)) {
1696 async_answer_0(&call, EINVAL);
[b4cbef1]1697 return EINVAL;
1698 }
[a35b458]1699
[79ae36dd]1700 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]1701 dataptr);
1702 if (msg == 0) {
[984a9ba]1703 async_answer_0(&call, EINVAL);
[b4cbef1]1704 return EINVAL;
1705 }
[a35b458]1706
[984a9ba]1707 errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
[b4cbef1]1708 IPC_FF_ROUTE_FROM_ME);
1709 if (retval != EOK) {
[ab9f443]1710 async_forget(msg);
[984a9ba]1711 async_answer_0(&call, retval);
[b4cbef1]1712 return retval;
1713 }
[a35b458]1714
[b7fd2a0]1715 errno_t rc;
[b4cbef1]1716 async_wait_for(msg, &rc);
[a35b458]1717
[b7fd2a0]1718 return (errno_t) rc;
[b4cbef1]1719}
1720
[79ae36dd]1721/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1722 *
1723 * If the current call is IPC_M_CONNECT_TO_ME then a new
1724 * async session is created for the accepted phone.
1725 *
1726 * @param mgmt Exchange management style.
1727 *
[8869f7b]1728 * @return New async session.
1729 * @return NULL on failure.
[79ae36dd]1730 *
1731 */
1732async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
1733{
1734 /* Accept the phone */
1735 ipc_call_t call;
[984a9ba]1736 async_get_call(&call);
1737
[eadaeae8]1738 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(call);
[a35b458]1739
[eadaeae8]1740 if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) ||
1741 !CAP_HANDLE_VALID((phandle))) {
[984a9ba]1742 async_answer_0(&call, EINVAL);
[79ae36dd]1743 return NULL;
1744 }
[a35b458]1745
[79ae36dd]1746 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1747 if (sess == NULL) {
[984a9ba]1748 async_answer_0(&call, ENOMEM);
[79ae36dd]1749 return NULL;
1750 }
[a35b458]1751
[566992e1]1752 sess->iface = 0;
[79ae36dd]1753 sess->mgmt = mgmt;
[01c3bb4]1754 sess->phone = phandle;
[79ae36dd]1755 sess->arg1 = 0;
1756 sess->arg2 = 0;
1757 sess->arg3 = 0;
[a35b458]1758
[58cbf8d5]1759 fibril_mutex_initialize(&sess->remote_state_mtx);
1760 sess->remote_state_data = NULL;
[a35b458]1761
[79ae36dd]1762 list_initialize(&sess->exch_list);
1763 fibril_mutex_initialize(&sess->mutex);
1764 atomic_set(&sess->refcnt, 0);
[a35b458]1765
[79ae36dd]1766 /* Acknowledge the connected phone */
[984a9ba]1767 async_answer_0(&call, EOK);
[a35b458]1768
[79ae36dd]1769 return sess;
1770}
1771
[8869f7b]1772/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1773 *
1774 * If the call is IPC_M_CONNECT_TO_ME then a new
1775 * async session is created. However, the phone is
1776 * not accepted automatically.
1777 *
1778 * @param mgmt Exchange management style.
1779 * @param call Call data.
1780 *
1781 * @return New async session.
1782 * @return NULL on failure.
1783 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
1784 *
1785 */
1786async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
1787{
[eadaeae8]1788 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(*call);
[a35b458]1789
[eadaeae8]1790 if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) ||
1791 !CAP_HANDLE_VALID((phandle)))
[8869f7b]1792 return NULL;
[a35b458]1793
[8869f7b]1794 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1795 if (sess == NULL)
1796 return NULL;
[a35b458]1797
[566992e1]1798 sess->iface = 0;
[8869f7b]1799 sess->mgmt = mgmt;
[01c3bb4]1800 sess->phone = phandle;
[8869f7b]1801 sess->arg1 = 0;
1802 sess->arg2 = 0;
1803 sess->arg3 = 0;
[a35b458]1804
[58cbf8d5]1805 fibril_mutex_initialize(&sess->remote_state_mtx);
1806 sess->remote_state_data = NULL;
[a35b458]1807
[8869f7b]1808 list_initialize(&sess->exch_list);
1809 fibril_mutex_initialize(&sess->mutex);
1810 atomic_set(&sess->refcnt, 0);
[a35b458]1811
[8869f7b]1812 return sess;
1813}
1814
[984a9ba]1815bool async_state_change_receive(ipc_call_t *call)
[2c4aa39]1816{
[984a9ba]1817 assert(call);
[a35b458]1818
[984a9ba]1819 async_get_call(call);
[a35b458]1820
[984a9ba]1821 if (IPC_GET_IMETHOD(*call) != IPC_M_STATE_CHANGE_AUTHORIZE)
[2c4aa39]1822 return false;
[a35b458]1823
[2c4aa39]1824 return true;
1825}
1826
[984a9ba]1827errno_t async_state_change_finalize(ipc_call_t *call, async_exch_t *other_exch)
[2c4aa39]1828{
[984a9ba]1829 assert(call);
1830
1831 return async_answer_1(call, EOK, CAP_HANDLE_RAW(other_exch->phone));
[2c4aa39]1832}
1833
[6b96dc06]1834__noreturn void async_manager(void)
[d73d992]1835{
[95838f1]1836 futex_lock(&async_futex);
[d73d992]1837 fibril_switch(FIBRIL_FROM_DEAD);
1838 __builtin_unreachable();
1839}
1840
[a46da63]1841/** @}
[b2951e2]1842 */
Note: See TracBrowser for help on using the repository browser.