source: mainline/uspace/lib/c/generic/async/server.c@ 205f1add

Last change on this file since 205f1add was 205f1add, checked in by Jakub Jermar <jakub@…>, 7 years ago

Get rid of sys/time.h

This commit moves the POSIX-like time functionality from libc's
sys/time.h to libposix and introduces C99-like or HelenOS-specific
interfaces to libc.

Specifically, use of sys/time.h, struct timeval, suseconds_t and
gettimeofday is replaced by time.h (C99), struct timespec (C99),
usec_t (HelenOS) and getuptime / getrealtime (HelenOS).

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