source: mainline/uspace/lib/c/generic/async/server.c@ 103939e

Last change on this file since 103939e was 5e801dc, checked in by GitHub <noreply@…>, 6 years ago

Indicate and enforce constness of hash table key in certain functions (#158)

The assumption here is that modifying key in the hash/equal functions in something completely unexpected, and not something you would ever want to do intentionally, so it makes sense to disallow it entirely to get that extra level of checking.

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