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

Last change on this file since 8f8818ac was 61eb2ce2, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 2 years ago

Make hash table operations immutable, because global mutable state is evil

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