source: mainline/uspace/lib/c/generic/async/server.c@ 241f1985

Last change on this file since 241f1985 was 241f1985, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

Correcting failure from previous merge

The commits from Michal Koutný from the branch system-daemon
where built on a old version of Helenos. Because of this
many types and API functions have changed. This commit
upgrades the merge code

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