source: mainline/uspace/lib/c/generic/async/server.c@ 8dab988

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

Put notifications into a dynamic queue instead of forgetting them.

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