source: mainline/uspace/lib/c/generic/async.c@ 2f7a564

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2f7a564 was 9934f7d, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Add extra argument to async connection handlers that can be used for passing
information from async_connect_to_me() to the handler.

  • Property mode set to 100644
File size: 58.7 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 *
79 * my_client_connection(icallid, *icall)
80 * {
81 * if (want_refuse) {
[64d2b10]82 * async_answer_0(icallid, ELIMIT);
[c07544d3]83 * return;
84 * }
[64d2b10]85 * async_answer_0(icallid, EOK);
[80649a91]86 *
[c07544d3]87 * callid = async_get_call(&call);
[0772aff]88 * somehow_handle_the_call(callid, call);
[64d2b10]89 * async_answer_2(callid, 1, 2, 3);
[53ca318]90 *
[c07544d3]91 * callid = async_get_call(&call);
92 * ...
93 * }
[a2cd194]94 *
[80649a91]95 */
[9591265]96
[64d2b10]97#define LIBC_ASYNC_C_
98#include <ipc/ipc.h>
[80649a91]99#include <async.h>
[64d2b10]100#undef LIBC_ASYNC_C_
101
102#include <futex.h>
[bc1f1c2]103#include <fibril.h>
[d9c8c81]104#include <adt/hash_table.h>
105#include <adt/list.h>
[80649a91]106#include <assert.h>
107#include <errno.h>
[daa90e8]108#include <sys/time.h>
[c042bdd]109#include <arch/barrier.h>
[0cc4313]110#include <bool.h>
[c7bbf029]111#include <malloc.h>
[79ae36dd]112#include <mem.h>
113#include <stdlib.h>
[e26a4633]114#include "private/async.h"
[80649a91]115
[79ae36dd]116#define CLIENT_HASH_TABLE_BUCKETS 32
117#define CONN_HASH_TABLE_BUCKETS 32
118
119/** Async framework global futex */
[fc42b28]120atomic_t async_futex = FUTEX_INITIALIZER;
[80649a91]121
[8619f25]122/** Number of threads waiting for IPC in the kernel. */
123atomic_t threads_in_ipc_wait = { 0 };
124
[79ae36dd]125/** Naming service session */
126async_sess_t *session_ns;
[01ff41c]127
[79ae36dd]128/** Call data */
[80649a91]129typedef struct {
130 link_t link;
[79ae36dd]131
[80649a91]132 ipc_callid_t callid;
133 ipc_call_t call;
134} msg_t;
135
[79ae36dd]136/* Client connection data */
[c80fdd0]137typedef struct {
138 link_t link;
[79ae36dd]139
140 sysarg_t in_task_hash;
141 atomic_t refcnt;
[c80fdd0]142 void *data;
143} client_t;
144
[79ae36dd]145/* Server connection data */
[80649a91]146typedef struct {
[49d072e]147 awaiter_t wdata;
[c07544d3]148
[e70bfa5]149 /** Hash table link. */
150 link_t link;
[c07544d3]151
[3c22f70]152 /** Incoming client task hash. */
153 sysarg_t in_task_hash;
[79ae36dd]154
[e70bfa5]155 /** Incoming phone hash. */
[96b02eb9]156 sysarg_t in_phone_hash;
[c07544d3]157
[23882034]158 /** Link to the client tracking structure. */
159 client_t *client;
[47b7006]160
[e70bfa5]161 /** Messages that should be delivered to this fibril. */
[c07544d3]162 link_t msg_queue;
163
[e70bfa5]164 /** Identification of the opening call. */
[80649a91]165 ipc_callid_t callid;
[e70bfa5]166 /** Call data of the opening call. */
[80649a91]167 ipc_call_t call;
[9934f7d]168 /** Local argument or NULL if none. */
169 void *carg;
[c07544d3]170
[e70bfa5]171 /** Identification of the closing call. */
172 ipc_callid_t close_callid;
[c07544d3]173
[e70bfa5]174 /** Fibril function that will be used to handle the connection. */
[9934f7d]175 async_client_conn_t cfibril;
[80649a91]176} connection_t;
177
[bc1f1c2]178/** Identifier of the incoming connection handled by the current fibril. */
[79ae36dd]179static fibril_local connection_t *fibril_connection;
[e70bfa5]180
[46eec3b]181static void *default_client_data_constructor(void)
182{
183 return NULL;
184}
185
186static void default_client_data_destructor(void *data)
187{
188}
189
190static async_client_data_ctor_t async_client_data_create =
191 default_client_data_constructor;
192static async_client_data_dtor_t async_client_data_destroy =
193 default_client_data_destructor;
194
195void async_set_client_data_constructor(async_client_data_ctor_t ctor)
196{
197 async_client_data_create = ctor;
198}
199
200void async_set_client_data_destructor(async_client_data_dtor_t dtor)
201{
202 async_client_data_destroy = dtor;
203}
204
[79ae36dd]205void *async_get_client_data(void)
[23882034]206{
[79ae36dd]207 assert(fibril_connection);
208 return fibril_connection->client->data;
[23882034]209}
210
[47b7006]211/** Default fibril function that gets called to handle new connection.
212 *
213 * This function is defined as a weak symbol - to be redefined in user code.
214 *
[9934f7d]215 * @param callid Hash of the incoming call.
216 * @param call Data of the incoming call.
217 * @param arg Local argument
[47b7006]218 *
219 */
[9934f7d]220static void default_client_connection(ipc_callid_t callid, ipc_call_t *call,
221 void *arg)
[47b7006]222{
223 ipc_answer_0(callid, ENOENT);
224}
[36c9234]225
[47b7006]226/** Default fibril function that gets called to handle interrupt notifications.
227 *
228 * This function is defined as a weak symbol - to be redefined in user code.
229 *
[9934f7d]230 * @param callid Hash of the incoming call.
231 * @param call Data of the incoming call.
232 * @param arg Local argument.
[47b7006]233 *
234 */
235static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
236{
237}
238
[79ae36dd]239static async_client_conn_t client_connection = default_client_connection;
[9934f7d]240static async_interrupt_handler_t interrupt_received = default_interrupt_received;
[da0c91e7]241
[79ae36dd]242/** Setter for client_connection function pointer.
243 *
244 * @param conn Function that will implement a new connection fibril.
245 *
246 */
247void async_set_client_connection(async_client_conn_t conn)
248{
249 client_connection = conn;
250}
251
252/** Setter for interrupt_received function pointer.
253 *
254 * @param intr Function that will implement a new interrupt
255 * notification fibril.
256 */
[9934f7d]257void async_set_interrupt_received(async_interrupt_handler_t intr)
[79ae36dd]258{
259 interrupt_received = intr;
260}
261
262/** Mutex protecting inactive_exch_list and avail_phone_cv.
263 *
264 */
265static FIBRIL_MUTEX_INITIALIZE(async_sess_mutex);
266
267/** List of all currently inactive exchanges.
268 *
269 */
270static LIST_INITIALIZE(inactive_exch_list);
271
272/** Condition variable to wait for a phone to become available.
273 *
274 */
275static FIBRIL_CONDVAR_INITIALIZE(avail_phone_cv);
276
[c80fdd0]277static hash_table_t client_hash_table;
[c07544d3]278static hash_table_t conn_hash_table;
279static LIST_INITIALIZE(timeout_list);
280
[47b7006]281static hash_index_t client_hash(unsigned long key[])
[c80fdd0]282{
283 assert(key);
[79ae36dd]284
[47b7006]285 return (((key[0]) >> 4) % CLIENT_HASH_TABLE_BUCKETS);
[c80fdd0]286}
287
288static int client_compare(unsigned long key[], hash_count_t keys, link_t *item)
289{
[79ae36dd]290 assert(key);
291 assert(item);
292
[47b7006]293 client_t *client = hash_table_get_instance(item, client_t, link);
294 return (key[0] == client->in_task_hash);
[c80fdd0]295}
296
297static void client_remove(link_t *item)
298{
299}
300
301/** Operations for the client hash table. */
302static hash_table_operations_t client_hash_table_ops = {
303 .hash = client_hash,
304 .compare = client_compare,
305 .remove_callback = client_remove
306};
[80649a91]307
[e70bfa5]308/** Compute hash into the connection hash table based on the source phone hash.
309 *
[c07544d3]310 * @param key Pointer to source phone hash.
311 *
312 * @return Index into the connection hash table.
[e70bfa5]313 *
314 */
[47b7006]315static hash_index_t conn_hash(unsigned long key[])
[450cd3a]316{
[80649a91]317 assert(key);
[79ae36dd]318
[47b7006]319 return (((key[0]) >> 4) % CONN_HASH_TABLE_BUCKETS);
[450cd3a]320}
[06502f7d]321
[e70bfa5]322/** Compare hash table item with a key.
323 *
[c07544d3]324 * @param key Array containing the source phone hash as the only item.
325 * @param keys Expected 1 but ignored.
326 * @param item Connection hash table item.
327 *
328 * @return True on match, false otherwise.
[e70bfa5]329 *
330 */
[80649a91]331static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
[450cd3a]332{
[79ae36dd]333 assert(key);
334 assert(item);
335
[47b7006]336 connection_t *conn = hash_table_get_instance(item, connection_t, link);
337 return (key[0] == conn->in_phone_hash);
[450cd3a]338}
[06502f7d]339
[80649a91]340static void conn_remove(link_t *item)
[450cd3a]341{
342}
343
[e70bfa5]344/** Operations for the connection hash table. */
[80649a91]345static hash_table_operations_t conn_hash_table_ops = {
346 .hash = conn_hash,
347 .compare = conn_compare,
348 .remove_callback = conn_remove
349};
350
[e70bfa5]351/** Sort in current fibril's timeout request.
[49d072e]352 *
[c07544d3]353 * @param wd Wait data of the current fibril.
354 *
[49d072e]355 */
[b6ee5b1]356void async_insert_timeout(awaiter_t *wd)
[49d072e]357{
[79ae36dd]358 assert(wd);
359
[f53cc81]360 wd->to_event.occurred = false;
361 wd->to_event.inlist = true;
[c07544d3]362
363 link_t *tmp = timeout_list.next;
[49d072e]364 while (tmp != &timeout_list) {
[47b7006]365 awaiter_t *cur
366 = list_get_instance(tmp, awaiter_t, to_event.link);
[c07544d3]367
[f53cc81]368 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
[49d072e]369 break;
[47b7006]370
[49d072e]371 tmp = tmp->next;
372 }
[c07544d3]373
[f53cc81]374 list_append(&wd->to_event.link, tmp);
[49d072e]375}
376
[e70bfa5]377/** Try to route a call to an appropriate connection fibril.
[80649a91]378 *
[36c9234]379 * If the proper connection fibril is found, a message with the call is added to
380 * its message queue. If the fibril was not active, it is activated and all
381 * timeouts are unregistered.
382 *
[c07544d3]383 * @param callid Hash of the incoming call.
384 * @param call Data of the incoming call.
385 *
386 * @return False if the call doesn't match any connection.
[47b7006]387 * @return True if the call was passed to the respective connection fibril.
[36c9234]388 *
[80649a91]389 */
[c07544d3]390static bool route_call(ipc_callid_t callid, ipc_call_t *call)
[450cd3a]391{
[79ae36dd]392 assert(call);
393
[01ff41c]394 futex_down(&async_futex);
[c07544d3]395
396 unsigned long key = call->in_phone_hash;
397 link_t *hlp = hash_table_find(&conn_hash_table, &key);
398
[80649a91]399 if (!hlp) {
[01ff41c]400 futex_up(&async_futex);
[c07544d3]401 return false;
[450cd3a]402 }
[c07544d3]403
404 connection_t *conn = hash_table_get_instance(hlp, connection_t, link);
405
406 msg_t *msg = malloc(sizeof(*msg));
407 if (!msg) {
408 futex_up(&async_futex);
409 return false;
410 }
411
[80649a91]412 msg->callid = callid;
413 msg->call = *call;
414 list_append(&msg->link, &conn->msg_queue);
[c07544d3]415
[228e490]416 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
[41269bd]417 conn->close_callid = callid;
[80649a91]418
[36c9234]419 /* If the connection fibril is waiting for an event, activate it */
[49d072e]420 if (!conn->wdata.active) {
[c07544d3]421
[49d072e]422 /* If in timeout list, remove it */
[f53cc81]423 if (conn->wdata.to_event.inlist) {
424 conn->wdata.to_event.inlist = false;
425 list_remove(&conn->wdata.to_event.link);
[49d072e]426 }
[c07544d3]427
428 conn->wdata.active = true;
[bc1f1c2]429 fibril_add_ready(conn->wdata.fid);
[80649a91]430 }
[c07544d3]431
[01ff41c]432 futex_up(&async_futex);
[c07544d3]433 return true;
434}
[80649a91]435
[c07544d3]436/** Notification fibril.
437 *
438 * When a notification arrives, a fibril with this implementing function is
439 * created. It calls interrupt_received() and does the final cleanup.
440 *
441 * @param arg Message structure pointer.
442 *
443 * @return Always zero.
444 *
445 */
446static int notification_fibril(void *arg)
447{
[79ae36dd]448 assert(arg);
449
[c07544d3]450 msg_t *msg = (msg_t *) arg;
451 interrupt_received(msg->callid, &msg->call);
452
453 free(msg);
454 return 0;
455}
456
457/** Process interrupt notification.
458 *
459 * A new fibril is created which would process the notification.
460 *
461 * @param callid Hash of the incoming call.
462 * @param call Data of the incoming call.
463 *
464 * @return False if an error occured.
465 * True if the call was passed to the notification fibril.
466 *
467 */
468static bool process_notification(ipc_callid_t callid, ipc_call_t *call)
469{
[79ae36dd]470 assert(call);
471
[c07544d3]472 futex_down(&async_futex);
473
474 msg_t *msg = malloc(sizeof(*msg));
475 if (!msg) {
476 futex_up(&async_futex);
477 return false;
478 }
479
480 msg->callid = callid;
481 msg->call = *call;
482
483 fid_t fid = fibril_create(notification_fibril, msg);
[86d7bfa]484 if (fid == 0) {
485 free(msg);
486 futex_up(&async_futex);
487 return false;
488 }
489
[c07544d3]490 fibril_add_ready(fid);
491
492 futex_up(&async_futex);
493 return true;
[80649a91]494}
495
[e70bfa5]496/** Return new incoming message for the current (fibril-local) connection.
497 *
[c07544d3]498 * @param call Storage where the incoming call data will be stored.
499 * @param usecs Timeout in microseconds. Zero denotes no timeout.
500 *
501 * @return If no timeout was specified, then a hash of the
502 * incoming call is returned. If a timeout is specified,
503 * then a hash of the incoming call is returned unless
504 * the timeout expires prior to receiving a message. In
505 * that case zero is returned.
[e70bfa5]506 *
507 */
[49d072e]508ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
[80649a91]509{
[79ae36dd]510 assert(call);
511 assert(fibril_connection);
[c07544d3]512
513 /* Why doing this?
[79ae36dd]514 * GCC 4.1.0 coughs on fibril_connection-> dereference.
[6c46350]515 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
[c07544d3]516 * I would never expect to find so many errors in
517 * a compiler.
[6c46350]518 */
[79ae36dd]519 connection_t *conn = fibril_connection;
[c07544d3]520
[01ff41c]521 futex_down(&async_futex);
[c07544d3]522
[49d072e]523 if (usecs) {
[f53cc81]524 gettimeofday(&conn->wdata.to_event.expires, NULL);
525 tv_add(&conn->wdata.to_event.expires, usecs);
[c07544d3]526 } else
[f53cc81]527 conn->wdata.to_event.inlist = false;
[c07544d3]528
[e70bfa5]529 /* If nothing in queue, wait until something arrives */
[6c46350]530 while (list_empty(&conn->msg_queue)) {
[8c8f8d6]531 if (conn->close_callid) {
532 /*
533 * Handle the case when the connection was already
534 * closed by the client but the server did not notice
535 * the first IPC_M_PHONE_HUNGUP call and continues to
536 * call async_get_call_timeout(). Repeat
[47b7006]537 * IPC_M_PHONE_HUNGUP until the caller notices.
[8c8f8d6]538 */
539 memset(call, 0, sizeof(ipc_call_t));
[228e490]540 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
[8c8f8d6]541 futex_up(&async_futex);
542 return conn->close_callid;
543 }
[47b7006]544
[085bd54]545 if (usecs)
[b6ee5b1]546 async_insert_timeout(&conn->wdata);
[c07544d3]547
548 conn->wdata.active = false;
549
[c7509e5]550 /*
551 * Note: the current fibril will be rescheduled either due to a
552 * timeout or due to an arriving message destined to it. In the
553 * former case, handle_expired_timeouts() and, in the latter
554 * case, route_call() will perform the wakeup.
555 */
[116d3f6f]556 fibril_switch(FIBRIL_TO_MANAGER);
[c07544d3]557
[e70bfa5]558 /*
[c07544d3]559 * Futex is up after getting back from async_manager.
560 * Get it again.
[c7509e5]561 */
[49d072e]562 futex_down(&async_futex);
[f53cc81]563 if ((usecs) && (conn->wdata.to_event.occurred)
[c07544d3]564 && (list_empty(&conn->msg_queue))) {
[e70bfa5]565 /* If we timed out -> exit */
[49d072e]566 futex_up(&async_futex);
567 return 0;
568 }
[450cd3a]569 }
570
[c07544d3]571 msg_t *msg = list_get_instance(conn->msg_queue.next, msg_t, link);
[80649a91]572 list_remove(&msg->link);
[c07544d3]573
574 ipc_callid_t callid = msg->callid;
[80649a91]575 *call = msg->call;
576 free(msg);
577
[01ff41c]578 futex_up(&async_futex);
[80649a91]579 return callid;
580}
581
[f2f0392]582/** Wrapper for client connection fibril.
583 *
[36c9234]584 * When a new connection arrives, a fibril with this implementing function is
[f2f0392]585 * created. It calls client_connection() and does the final cleanup.
[a2cd194]586 *
[c07544d3]587 * @param arg Connection structure pointer.
588 *
589 * @return Always zero.
[a2cd194]590 *
591 */
[c07544d3]592static int connection_fibril(void *arg)
[80649a91]593{
[79ae36dd]594 assert(arg);
595
[c07544d3]596 /*
[c80fdd0]597 * Setup fibril-local connection pointer.
[c07544d3]598 */
[79ae36dd]599 fibril_connection = (connection_t *) arg;
[47b7006]600
601 futex_down(&async_futex);
602
[c80fdd0]603 /*
604 * Add our reference for the current connection in the client task
605 * tracking structure. If this is the first reference, create and
606 * hash in a new tracking structure.
607 */
[47b7006]608
[79ae36dd]609 unsigned long key = fibril_connection->in_task_hash;
[47b7006]610 link_t *lnk = hash_table_find(&client_hash_table, &key);
611
612 client_t *client;
613
[c80fdd0]614 if (lnk) {
[47b7006]615 client = hash_table_get_instance(lnk, client_t, link);
[79ae36dd]616 atomic_inc(&client->refcnt);
[c80fdd0]617 } else {
[47b7006]618 client = malloc(sizeof(client_t));
619 if (!client) {
[79ae36dd]620 ipc_answer_0(fibril_connection->callid, ENOMEM);
[c80fdd0]621 futex_up(&async_futex);
622 return 0;
623 }
[47b7006]624
[79ae36dd]625 client->in_task_hash = fibril_connection->in_task_hash;
[47b7006]626 client->data = async_client_data_create();
627
[79ae36dd]628 atomic_set(&client->refcnt, 1);
[47b7006]629 hash_table_insert(&client_hash_table, &key, &client->link);
[c80fdd0]630 }
[47b7006]631
[c80fdd0]632 futex_up(&async_futex);
[47b7006]633
[79ae36dd]634 fibril_connection->client = client;
[47b7006]635
[c80fdd0]636 /*
637 * Call the connection handler function.
638 */
[79ae36dd]639 fibril_connection->cfibril(fibril_connection->callid,
[9934f7d]640 &fibril_connection->call, fibril_connection->carg);
[a46da63]641
[c80fdd0]642 /*
643 * Remove the reference for this client task connection.
644 */
[47b7006]645 bool destroy;
646
[01ff41c]647 futex_down(&async_futex);
[47b7006]648
[79ae36dd]649 if (atomic_predec(&client->refcnt) == 0) {
[c80fdd0]650 hash_table_remove(&client_hash_table, &key, 1);
[8526e585]651 destroy = true;
[47b7006]652 } else
653 destroy = false;
654
[c80fdd0]655 futex_up(&async_futex);
[47b7006]656
[8526e585]657 if (destroy) {
[47b7006]658 if (client->data)
659 async_client_data_destroy(client->data);
660
661 free(client);
[8526e585]662 }
[47b7006]663
[c80fdd0]664 /*
665 * Remove myself from the connection hash table.
666 */
667 futex_down(&async_futex);
[79ae36dd]668 key = fibril_connection->in_phone_hash;
[a2cd194]669 hash_table_remove(&conn_hash_table, &key, 1);
[01ff41c]670 futex_up(&async_futex);
[a46da63]671
[c80fdd0]672 /*
673 * Answer all remaining messages with EHANGUP.
674 */
[79ae36dd]675 while (!list_empty(&fibril_connection->msg_queue)) {
[47b7006]676 msg_t *msg =
[79ae36dd]677 list_get_instance(fibril_connection->msg_queue.next, msg_t,
[47b7006]678 link);
[c07544d3]679
[a2cd194]680 list_remove(&msg->link);
[b74959bd]681 ipc_answer_0(msg->callid, EHANGUP);
[a2cd194]682 free(msg);
683 }
[c07544d3]684
[c80fdd0]685 /*
686 * If the connection was hung-up, answer the last call,
687 * i.e. IPC_M_PHONE_HUNGUP.
688 */
[79ae36dd]689 if (fibril_connection->close_callid)
690 ipc_answer_0(fibril_connection->close_callid, EOK);
[a46da63]691
[79ae36dd]692 free(fibril_connection);
[a46da63]693 return 0;
[80649a91]694}
695
[f2f0392]696/** Create a new fibril for a new connection.
[80649a91]697 *
[79ae36dd]698 * Create new fibril for connection, fill in connection structures and insert
[f2f0392]699 * it into the hash table, so that later we can easily do routing of messages to
700 * particular fibrils.
[53ca318]701 *
[3c22f70]702 * @param in_task_hash Identification of the incoming connection.
[c07544d3]703 * @param in_phone_hash Identification of the incoming connection.
704 * @param callid Hash of the opening IPC_M_CONNECT_ME_TO call.
705 * If callid is zero, the connection was opened by
706 * accepting the IPC_M_CONNECT_TO_ME call and this function
707 * is called directly by the server.
708 * @param call Call data of the opening call.
709 * @param cfibril Fibril function that should be called upon opening the
710 * connection.
[9934f7d]711 * @param carg Extra argument to pass to the connection fibril
[c07544d3]712 *
713 * @return New fibril id or NULL on failure.
[36c9234]714 *
[80649a91]715 */
[3c22f70]716fid_t async_new_connection(sysarg_t in_task_hash, sysarg_t in_phone_hash,
717 ipc_callid_t callid, ipc_call_t *call,
[9934f7d]718 async_client_conn_t cfibril, void *carg)
[80649a91]719{
[c07544d3]720 connection_t *conn = malloc(sizeof(*conn));
[80649a91]721 if (!conn) {
[6675c70]722 if (callid)
[b74959bd]723 ipc_answer_0(callid, ENOMEM);
[47b7006]724
[0b4a67a]725 return (uintptr_t) NULL;
[80649a91]726 }
[c07544d3]727
[3c22f70]728 conn->in_task_hash = in_task_hash;
[44c6d88d]729 conn->in_phone_hash = in_phone_hash;
[80649a91]730 list_initialize(&conn->msg_queue);
731 conn->callid = callid;
[c4702804]732 conn->close_callid = 0;
[9934f7d]733 conn->carg = carg;
[c07544d3]734
[eaf34f7]735 if (call)
736 conn->call = *call;
[6b21292]737
[c07544d3]738 /* We will activate the fibril ASAP */
739 conn->wdata.active = true;
740 conn->cfibril = cfibril;
[bc1f1c2]741 conn->wdata.fid = fibril_create(connection_fibril, conn);
[c07544d3]742
[86d7bfa]743 if (conn->wdata.fid == 0) {
[80649a91]744 free(conn);
[86d7bfa]745
[6675c70]746 if (callid)
[b74959bd]747 ipc_answer_0(callid, ENOMEM);
[86d7bfa]748
[0b4a67a]749 return (uintptr_t) NULL;
[80649a91]750 }
[6b21292]751
[36c9234]752 /* Add connection to the connection hash table */
[9db9b10]753 unsigned long key = conn->in_phone_hash;
[c07544d3]754
[01ff41c]755 futex_down(&async_futex);
[80649a91]756 hash_table_insert(&conn_hash_table, &key, &conn->link);
[01ff41c]757 futex_up(&async_futex);
[6b21292]758
[bc1f1c2]759 fibril_add_ready(conn->wdata.fid);
[6b21292]760
[bc1f1c2]761 return conn->wdata.fid;
[80649a91]762}
763
[36c9234]764/** Handle a call that was received.
765 *
766 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
767 * Otherwise the call is routed to its connection fibril.
768 *
[c07544d3]769 * @param callid Hash of the incoming call.
770 * @param call Data of the incoming call.
[6b21292]771 *
[36c9234]772 */
[80649a91]773static void handle_call(ipc_callid_t callid, ipc_call_t *call)
774{
[79ae36dd]775 assert(call);
776
[47b7006]777 /* Unrouted call - take some default action */
[15039b67]778 if ((callid & IPC_CALLID_NOTIFICATION)) {
[c07544d3]779 process_notification(callid, call);
[47b7006]780 return;
[6b21292]781 }
782
[228e490]783 switch (IPC_GET_IMETHOD(*call)) {
[2c0e5d2]784 case IPC_M_CONNECT_ME:
[80649a91]785 case IPC_M_CONNECT_ME_TO:
[47b7006]786 /* Open new connection with fibril, etc. */
[3c22f70]787 async_new_connection(call->in_task_hash, IPC_GET_ARG5(*call),
[9934f7d]788 callid, call, client_connection, NULL);
[47b7006]789 return;
[80649a91]790 }
[6b21292]791
[36c9234]792 /* Try to route the call through the connection hash table */
[44c6d88d]793 if (route_call(callid, call))
[47b7006]794 return;
[6b21292]795
[44c6d88d]796 /* Unknown call from unknown phone - hang it up */
[b74959bd]797 ipc_answer_0(callid, EHANGUP);
[450cd3a]798}
799
[f2f0392]800/** Fire all timeouts that expired. */
[c042bdd]801static void handle_expired_timeouts(void)
802{
803 struct timeval tv;
[36c9234]804 gettimeofday(&tv, NULL);
[c07544d3]805
[c042bdd]806 futex_down(&async_futex);
[c07544d3]807
808 link_t *cur = timeout_list.next;
[c042bdd]809 while (cur != &timeout_list) {
[47b7006]810 awaiter_t *waiter =
811 list_get_instance(cur, awaiter_t, to_event.link);
[c07544d3]812
[f53cc81]813 if (tv_gt(&waiter->to_event.expires, &tv))
[c042bdd]814 break;
[47b7006]815
[c042bdd]816 cur = cur->next;
[47b7006]817
[f53cc81]818 list_remove(&waiter->to_event.link);
819 waiter->to_event.inlist = false;
820 waiter->to_event.occurred = true;
[c07544d3]821
[36c9234]822 /*
[c07544d3]823 * Redundant condition?
824 * The fibril should not be active when it gets here.
[c042bdd]825 */
[49d072e]826 if (!waiter->active) {
[c07544d3]827 waiter->active = true;
[bc1f1c2]828 fibril_add_ready(waiter->fid);
[c042bdd]829 }
830 }
[c07544d3]831
[c042bdd]832 futex_up(&async_futex);
833}
834
[36c9234]835/** Endless loop dispatching incoming calls and answers.
836 *
[c07544d3]837 * @return Never returns.
838 *
[36c9234]839 */
[085bd54]840static int async_manager_worker(void)
[80649a91]841{
[c07544d3]842 while (true) {
[116d3f6f]843 if (fibril_switch(FIBRIL_FROM_MANAGER)) {
[47b7006]844 futex_up(&async_futex);
[36c9234]845 /*
846 * async_futex is always held when entering a manager
847 * fibril.
[a46da63]848 */
[80649a91]849 continue;
850 }
[c07544d3]851
[c042bdd]852 futex_down(&async_futex);
[c07544d3]853
854 suseconds_t timeout;
[c042bdd]855 if (!list_empty(&timeout_list)) {
[cc27c8c5]856 awaiter_t *waiter = list_get_instance(timeout_list.next,
[f53cc81]857 awaiter_t, to_event.link);
[c07544d3]858
859 struct timeval tv;
[bc1f1c2]860 gettimeofday(&tv, NULL);
[c07544d3]861
[f53cc81]862 if (tv_gteq(&tv, &waiter->to_event.expires)) {
[6c46350]863 futex_up(&async_futex);
[c042bdd]864 handle_expired_timeouts();
865 continue;
866 } else
[47b7006]867 timeout = tv_sub(&waiter->to_event.expires, &tv);
[c042bdd]868 } else
[0b99e40]869 timeout = SYNCH_NO_TIMEOUT;
[c07544d3]870
[c042bdd]871 futex_up(&async_futex);
[47b7006]872
[8619f25]873 atomic_inc(&threads_in_ipc_wait);
[c07544d3]874
875 ipc_call_t call;
[cc27c8c5]876 ipc_callid_t callid = ipc_wait_cycle(&call, timeout,
877 SYNCH_FLAGS_NONE);
[c07544d3]878
[8619f25]879 atomic_dec(&threads_in_ipc_wait);
[47b7006]880
[0b99e40]881 if (!callid) {
[c042bdd]882 handle_expired_timeouts();
[0b99e40]883 continue;
884 }
[c07544d3]885
886 if (callid & IPC_CALLID_ANSWERED)
[80649a91]887 continue;
[c07544d3]888
[80649a91]889 handle_call(callid, &call);
890 }
[a46da63]891
892 return 0;
[80649a91]893}
894
[36c9234]895/** Function to start async_manager as a standalone fibril.
[c07544d3]896 *
[36c9234]897 * When more kernel threads are used, one async manager should exist per thread.
898 *
[c07544d3]899 * @param arg Unused.
900 * @return Never returns.
[36c9234]901 *
[a2cd194]902 */
[9591265]903static int async_manager_fibril(void *arg)
[80649a91]904{
[a46da63]905 futex_up(&async_futex);
[c07544d3]906
[36c9234]907 /*
908 * async_futex is always locked when entering manager
909 */
[085bd54]910 async_manager_worker();
[a46da63]911
912 return 0;
[80649a91]913}
[450cd3a]914
[36c9234]915/** Add one manager to manager list. */
[80649a91]916void async_create_manager(void)
[450cd3a]917{
[c07544d3]918 fid_t fid = fibril_create(async_manager_fibril, NULL);
[86d7bfa]919 if (fid != 0)
920 fibril_add_manager(fid);
[80649a91]921}
922
923/** Remove one manager from manager list */
924void async_destroy_manager(void)
925{
[bc1f1c2]926 fibril_remove_manager();
[80649a91]927}
928
[36c9234]929/** Initialize the async framework.
930 *
931 */
[47b7006]932void __async_init(void)
[80649a91]933{
[79ae36dd]934 if (!hash_table_create(&client_hash_table, CLIENT_HASH_TABLE_BUCKETS,
935 1, &client_hash_table_ops))
[47b7006]936 abort();
[80649a91]937
[79ae36dd]938 if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_BUCKETS,
939 1, &conn_hash_table_ops))
[47b7006]940 abort();
[79ae36dd]941
942 session_ns = (async_sess_t *) malloc(sizeof(async_sess_t));
943 if (session_ns == NULL)
944 abort();
945
946 session_ns->mgmt = EXCHANGE_ATOMIC;
947 session_ns->phone = PHONE_NS;
948 session_ns->arg1 = 0;
949 session_ns->arg2 = 0;
950 session_ns->arg3 = 0;
951
952 list_initialize(&session_ns->exch_list);
953 fibril_mutex_initialize(&session_ns->mutex);
954 atomic_set(&session_ns->refcnt, 0);
[450cd3a]955}
[01ff41c]956
[36c9234]957/** Reply received callback.
[01ff41c]958 *
[36c9234]959 * This function is called whenever a reply for an asynchronous message sent out
960 * by the asynchronous framework is received.
961 *
962 * Notify the fibril which is waiting for this message that it has arrived.
963 *
[c07544d3]964 * @param arg Pointer to the asynchronous message record.
965 * @param retval Value returned in the answer.
966 * @param data Call data of the answer.
[47b7006]967 *
[01ff41c]968 */
[79ae36dd]969void reply_received(void *arg, int retval, ipc_call_t *data)
[01ff41c]970{
[79ae36dd]971 assert(arg);
972
[9db9b10]973 futex_down(&async_futex);
974
[c07544d3]975 amsg_t *msg = (amsg_t *) arg;
[01ff41c]976 msg->retval = retval;
[c07544d3]977
[36c9234]978 /* Copy data after futex_down, just in case the call was detached */
[9db9b10]979 if ((msg->dataptr) && (data))
[c07544d3]980 *msg->dataptr = *data;
981
[c042bdd]982 write_barrier();
[c07544d3]983
[c042bdd]984 /* Remove message from timeout list */
[f53cc81]985 if (msg->wdata.to_event.inlist)
986 list_remove(&msg->wdata.to_event.link);
[c07544d3]987
988 msg->done = true;
[36c9234]989 if (!msg->wdata.active) {
[c07544d3]990 msg->wdata.active = true;
[bc1f1c2]991 fibril_add_ready(msg->wdata.fid);
[01ff41c]992 }
[c07544d3]993
[01ff41c]994 futex_up(&async_futex);
995}
996
[36c9234]997/** Send message and return id of the sent message.
998 *
999 * The return value can be used as input for async_wait() to wait for
1000 * completion.
[01ff41c]1001 *
[79ae36dd]1002 * @param exch Exchange for sending the message.
1003 * @param imethod Service-defined interface and method.
[c07544d3]1004 * @param arg1 Service-defined payload argument.
1005 * @param arg2 Service-defined payload argument.
1006 * @param arg3 Service-defined payload argument.
1007 * @param arg4 Service-defined payload argument.
1008 * @param dataptr If non-NULL, storage where the reply data will be
1009 * stored.
1010 *
1011 * @return Hash of the sent message or 0 on error.
[36c9234]1012 *
[01ff41c]1013 */
[79ae36dd]1014aid_t async_send_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
[96b02eb9]1015 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
[01ff41c]1016{
[79ae36dd]1017 if (exch == NULL)
1018 return 0;
[c07544d3]1019
[79ae36dd]1020 amsg_t *msg = malloc(sizeof(amsg_t));
1021 if (msg == NULL)
[c07544d3]1022 return 0;
[6b21292]1023
[c07544d3]1024 msg->done = false;
[01ff41c]1025 msg->dataptr = dataptr;
[6b21292]1026
[f53cc81]1027 msg->wdata.to_event.inlist = false;
[47b7006]1028
1029 /*
1030 * We may sleep in the next method,
1031 * but it will use its own means
1032 */
[c07544d3]1033 msg->wdata.active = true;
1034
[79ae36dd]1035 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4, msg,
[c07544d3]1036 reply_received, true);
[6b21292]1037
[01ff41c]1038 return (aid_t) msg;
1039}
1040
[90f5d64]1041/** Send message and return id of the sent message
1042 *
[36c9234]1043 * The return value can be used as input for async_wait() to wait for
1044 * completion.
1045 *
[79ae36dd]1046 * @param exch Exchange for sending the message.
1047 * @param imethod Service-defined interface and method.
[c07544d3]1048 * @param arg1 Service-defined payload argument.
1049 * @param arg2 Service-defined payload argument.
1050 * @param arg3 Service-defined payload argument.
1051 * @param arg4 Service-defined payload argument.
1052 * @param arg5 Service-defined payload argument.
1053 * @param dataptr If non-NULL, storage where the reply data will be
1054 * stored.
1055 *
1056 * @return Hash of the sent message or 0 on error.
[36c9234]1057 *
[90f5d64]1058 */
[79ae36dd]1059aid_t async_send_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
[96b02eb9]1060 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
[0cc4313]1061 ipc_call_t *dataptr)
[90f5d64]1062{
[79ae36dd]1063 if (exch == NULL)
1064 return 0;
1065
[47b7006]1066 amsg_t *msg = malloc(sizeof(amsg_t));
[6b21292]1067
[79ae36dd]1068 if (msg == NULL)
[c07544d3]1069 return 0;
1070
1071 msg->done = false;
[90f5d64]1072 msg->dataptr = dataptr;
[6b21292]1073
[f53cc81]1074 msg->wdata.to_event.inlist = false;
[47b7006]1075
1076 /*
1077 * We may sleep in the next method,
1078 * but it will use its own means
1079 */
[c07544d3]1080 msg->wdata.active = true;
[6b21292]1081
[79ae36dd]1082 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4, arg5,
1083 msg, reply_received, true);
[6b21292]1084
[90f5d64]1085 return (aid_t) msg;
1086}
1087
[36c9234]1088/** Wait for a message sent by the async framework.
[01ff41c]1089 *
[c07544d3]1090 * @param amsgid Hash of the message to wait for.
1091 * @param retval Pointer to storage where the retval of the answer will
1092 * be stored.
1093 *
[01ff41c]1094 */
[96b02eb9]1095void async_wait_for(aid_t amsgid, sysarg_t *retval)
[01ff41c]1096{
[79ae36dd]1097 assert(amsgid);
1098
[01ff41c]1099 amsg_t *msg = (amsg_t *) amsgid;
[c07544d3]1100
[01ff41c]1101 futex_down(&async_futex);
1102 if (msg->done) {
1103 futex_up(&async_futex);
1104 goto done;
1105 }
[c07544d3]1106
[bc1f1c2]1107 msg->wdata.fid = fibril_get_id();
[c07544d3]1108 msg->wdata.active = false;
[f53cc81]1109 msg->wdata.to_event.inlist = false;
[c07544d3]1110
[36c9234]1111 /* Leave the async_futex locked when entering this function */
[116d3f6f]1112 fibril_switch(FIBRIL_TO_MANAGER);
[c07544d3]1113
1114 /* Futex is up automatically after fibril_switch */
1115
[01ff41c]1116done:
1117 if (retval)
1118 *retval = msg->retval;
[c07544d3]1119
[01ff41c]1120 free(msg);
1121}
[0b99e40]1122
[36c9234]1123/** Wait for a message sent by the async framework, timeout variant.
[c042bdd]1124 *
[c07544d3]1125 * @param amsgid Hash of the message to wait for.
1126 * @param retval Pointer to storage where the retval of the answer will
1127 * be stored.
1128 * @param timeout Timeout in microseconds.
1129 *
1130 * @return Zero on success, ETIMEOUT if the timeout has expired.
[c042bdd]1131 *
1132 */
[96b02eb9]1133int async_wait_timeout(aid_t amsgid, sysarg_t *retval, suseconds_t timeout)
[c042bdd]1134{
[79ae36dd]1135 assert(amsgid);
1136
[c042bdd]1137 amsg_t *msg = (amsg_t *) amsgid;
[c07544d3]1138
[86029498]1139 /* TODO: Let it go through the event read at least once */
1140 if (timeout < 0)
1141 return ETIMEOUT;
[c07544d3]1142
[c042bdd]1143 futex_down(&async_futex);
1144 if (msg->done) {
1145 futex_up(&async_futex);
1146 goto done;
1147 }
[c07544d3]1148
[f53cc81]1149 gettimeofday(&msg->wdata.to_event.expires, NULL);
1150 tv_add(&msg->wdata.to_event.expires, timeout);
[c07544d3]1151
[bc1f1c2]1152 msg->wdata.fid = fibril_get_id();
[c07544d3]1153 msg->wdata.active = false;
[b6ee5b1]1154 async_insert_timeout(&msg->wdata);
[c07544d3]1155
[36c9234]1156 /* Leave the async_futex locked when entering this function */
[116d3f6f]1157 fibril_switch(FIBRIL_TO_MANAGER);
[c07544d3]1158
1159 /* Futex is up automatically after fibril_switch */
1160
[c042bdd]1161 if (!msg->done)
1162 return ETIMEOUT;
[c07544d3]1163
[c042bdd]1164done:
1165 if (retval)
1166 *retval = msg->retval;
[c07544d3]1167
[c042bdd]1168 free(msg);
[c07544d3]1169
[c042bdd]1170 return 0;
1171}
[0b99e40]1172
[36c9234]1173/** Wait for specified time.
[44c6d88d]1174 *
[36c9234]1175 * The current fibril is suspended but the thread continues to execute.
1176 *
[c07544d3]1177 * @param timeout Duration of the wait in microseconds.
1178 *
[44c6d88d]1179 */
1180void async_usleep(suseconds_t timeout)
1181{
[47b7006]1182 amsg_t *msg = malloc(sizeof(amsg_t));
[44c6d88d]1183
1184 if (!msg)
1185 return;
[6b21292]1186
[bc1f1c2]1187 msg->wdata.fid = fibril_get_id();
[c07544d3]1188 msg->wdata.active = false;
[6b21292]1189
[f53cc81]1190 gettimeofday(&msg->wdata.to_event.expires, NULL);
1191 tv_add(&msg->wdata.to_event.expires, timeout);
[6b21292]1192
[44c6d88d]1193 futex_down(&async_futex);
[c07544d3]1194
[b6ee5b1]1195 async_insert_timeout(&msg->wdata);
[c07544d3]1196
[36c9234]1197 /* Leave the async_futex locked when entering this function */
[116d3f6f]1198 fibril_switch(FIBRIL_TO_MANAGER);
[c07544d3]1199
1200 /* Futex is up automatically after fibril_switch() */
1201
[44c6d88d]1202 free(msg);
1203}
[da0c91e7]1204
[0cc4313]1205/** Pseudo-synchronous message sending - fast version.
1206 *
1207 * Send message asynchronously and return only after the reply arrives.
1208 *
1209 * This function can only transfer 4 register payload arguments. For
1210 * transferring more arguments, see the slower async_req_slow().
1211 *
[79ae36dd]1212 * @param exch Exchange for sending the message.
1213 * @param imethod Interface and method of the call.
[c07544d3]1214 * @param arg1 Service-defined payload argument.
1215 * @param arg2 Service-defined payload argument.
1216 * @param arg3 Service-defined payload argument.
1217 * @param arg4 Service-defined payload argument.
1218 * @param r1 If non-NULL, storage for the 1st reply argument.
1219 * @param r2 If non-NULL, storage for the 2nd reply argument.
1220 * @param r3 If non-NULL, storage for the 3rd reply argument.
1221 * @param r4 If non-NULL, storage for the 4th reply argument.
1222 * @param r5 If non-NULL, storage for the 5th reply argument.
1223 *
1224 * @return Return code of the reply or a negative error code.
1225 *
[0cc4313]1226 */
[79ae36dd]1227sysarg_t async_req_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
[96b02eb9]1228 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
1229 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
[085bd54]1230{
[79ae36dd]1231 if (exch == NULL)
1232 return ENOENT;
1233
[0cc4313]1234 ipc_call_t result;
[79ae36dd]1235 aid_t aid = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
[0cc4313]1236 &result);
[c07544d3]1237
[96b02eb9]1238 sysarg_t rc;
[79ae36dd]1239 async_wait_for(aid, &rc);
[c07544d3]1240
1241 if (r1)
[0cc4313]1242 *r1 = IPC_GET_ARG1(result);
[c07544d3]1243
[0cc4313]1244 if (r2)
1245 *r2 = IPC_GET_ARG2(result);
[c07544d3]1246
[0cc4313]1247 if (r3)
1248 *r3 = IPC_GET_ARG3(result);
[c07544d3]1249
[0cc4313]1250 if (r4)
1251 *r4 = IPC_GET_ARG4(result);
[c07544d3]1252
[0cc4313]1253 if (r5)
1254 *r5 = IPC_GET_ARG5(result);
[c07544d3]1255
[0cc4313]1256 return rc;
[085bd54]1257}
1258
[0cc4313]1259/** Pseudo-synchronous message sending - slow version.
1260 *
1261 * Send message asynchronously and return only after the reply arrives.
1262 *
[79ae36dd]1263 * @param exch Exchange for sending the message.
1264 * @param imethod Interface and method of the call.
[c07544d3]1265 * @param arg1 Service-defined payload argument.
1266 * @param arg2 Service-defined payload argument.
1267 * @param arg3 Service-defined payload argument.
1268 * @param arg4 Service-defined payload argument.
1269 * @param arg5 Service-defined payload argument.
1270 * @param r1 If non-NULL, storage for the 1st reply argument.
1271 * @param r2 If non-NULL, storage for the 2nd reply argument.
1272 * @param r3 If non-NULL, storage for the 3rd reply argument.
1273 * @param r4 If non-NULL, storage for the 4th reply argument.
1274 * @param r5 If non-NULL, storage for the 5th reply argument.
1275 *
1276 * @return Return code of the reply or a negative error code.
1277 *
[0cc4313]1278 */
[79ae36dd]1279sysarg_t async_req_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
[96b02eb9]1280 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
1281 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
[085bd54]1282{
[79ae36dd]1283 if (exch == NULL)
1284 return ENOENT;
1285
[0cc4313]1286 ipc_call_t result;
[79ae36dd]1287 aid_t aid = async_send_5(exch, imethod, arg1, arg2, arg3, arg4, arg5,
[0cc4313]1288 &result);
[c07544d3]1289
[96b02eb9]1290 sysarg_t rc;
[79ae36dd]1291 async_wait_for(aid, &rc);
[c07544d3]1292
1293 if (r1)
[0cc4313]1294 *r1 = IPC_GET_ARG1(result);
[c07544d3]1295
[0cc4313]1296 if (r2)
1297 *r2 = IPC_GET_ARG2(result);
[c07544d3]1298
[0cc4313]1299 if (r3)
1300 *r3 = IPC_GET_ARG3(result);
[c07544d3]1301
[0cc4313]1302 if (r4)
1303 *r4 = IPC_GET_ARG4(result);
[c07544d3]1304
[0cc4313]1305 if (r5)
1306 *r5 = IPC_GET_ARG5(result);
[c07544d3]1307
[0cc4313]1308 return rc;
[085bd54]1309}
[b2951e2]1310
[79ae36dd]1311void async_msg_0(async_exch_t *exch, sysarg_t imethod)
[64d2b10]1312{
[79ae36dd]1313 if (exch != NULL)
1314 ipc_call_async_0(exch->phone, imethod, NULL, NULL, true);
[64d2b10]1315}
1316
[79ae36dd]1317void async_msg_1(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1)
[64d2b10]1318{
[79ae36dd]1319 if (exch != NULL)
1320 ipc_call_async_1(exch->phone, imethod, arg1, NULL, NULL, true);
[64d2b10]1321}
1322
[79ae36dd]1323void async_msg_2(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1324 sysarg_t arg2)
[64d2b10]1325{
[79ae36dd]1326 if (exch != NULL)
1327 ipc_call_async_2(exch->phone, imethod, arg1, arg2, NULL, NULL,
1328 true);
[64d2b10]1329}
1330
[79ae36dd]1331void async_msg_3(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1332 sysarg_t arg2, sysarg_t arg3)
[64d2b10]1333{
[79ae36dd]1334 if (exch != NULL)
1335 ipc_call_async_3(exch->phone, imethod, arg1, arg2, arg3, NULL,
1336 NULL, true);
[64d2b10]1337}
1338
[79ae36dd]1339void async_msg_4(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1340 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
[64d2b10]1341{
[79ae36dd]1342 if (exch != NULL)
1343 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4,
1344 NULL, NULL, true);
[64d2b10]1345}
1346
[79ae36dd]1347void async_msg_5(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1348 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
[64d2b10]1349{
[79ae36dd]1350 if (exch != NULL)
1351 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4,
1352 arg5, NULL, NULL, true);
[64d2b10]1353}
1354
1355sysarg_t async_answer_0(ipc_callid_t callid, sysarg_t retval)
1356{
1357 return ipc_answer_0(callid, retval);
1358}
1359
1360sysarg_t async_answer_1(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1)
1361{
1362 return ipc_answer_1(callid, retval, arg1);
1363}
1364
1365sysarg_t async_answer_2(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
1366 sysarg_t arg2)
1367{
1368 return ipc_answer_2(callid, retval, arg1, arg2);
1369}
1370
1371sysarg_t async_answer_3(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
1372 sysarg_t arg2, sysarg_t arg3)
1373{
1374 return ipc_answer_3(callid, retval, arg1, arg2, arg3);
1375}
1376
1377sysarg_t async_answer_4(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
1378 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1379{
1380 return ipc_answer_4(callid, retval, arg1, arg2, arg3, arg4);
1381}
1382
1383sysarg_t async_answer_5(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
1384 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
1385{
1386 return ipc_answer_5(callid, retval, arg1, arg2, arg3, arg4, arg5);
1387}
1388
[79ae36dd]1389int async_forward_fast(ipc_callid_t callid, async_exch_t *exch,
1390 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
[64d2b10]1391{
[79ae36dd]1392 if (exch == NULL)
1393 return ENOENT;
1394
1395 return ipc_forward_fast(callid, exch->phone, imethod, arg1, arg2, mode);
[64d2b10]1396}
1397
[79ae36dd]1398int async_forward_slow(ipc_callid_t callid, async_exch_t *exch,
1399 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
1400 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
[64d2b10]1401{
[79ae36dd]1402 if (exch == NULL)
1403 return ENOENT;
1404
1405 return ipc_forward_slow(callid, exch->phone, imethod, arg1, arg2, arg3,
1406 arg4, arg5, mode);
[64d2b10]1407}
1408
[007e6efa]1409/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
1410 *
1411 * Ask through phone for a new connection to some service.
1412 *
[79ae36dd]1413 * @param exch Exchange for sending the message.
[007e6efa]1414 * @param arg1 User defined argument.
1415 * @param arg2 User defined argument.
1416 * @param arg3 User defined argument.
1417 * @param client_receiver Connection handing routine.
1418 *
[79ae36dd]1419 * @return Zero on success or a negative error code.
[007e6efa]1420 *
1421 */
[79ae36dd]1422int async_connect_to_me(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
[9934f7d]1423 sysarg_t arg3, async_client_conn_t client_receiver, void *carg)
[007e6efa]1424{
[79ae36dd]1425 if (exch == NULL)
1426 return ENOENT;
1427
[007e6efa]1428 sysarg_t task_hash;
1429 sysarg_t phone_hash;
[79ae36dd]1430 int rc = async_req_3_5(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
[007e6efa]1431 NULL, NULL, NULL, &task_hash, &phone_hash);
1432 if (rc != EOK)
1433 return rc;
1434
1435 if (client_receiver != NULL)
1436 async_new_connection(task_hash, phone_hash, 0, NULL,
[9934f7d]1437 client_receiver, carg);
[007e6efa]1438
1439 return EOK;
1440}
1441
[79ae36dd]1442/** Wrapper for making IPC_M_CONNECT_ME calls using the async framework.
[007e6efa]1443 *
[79ae36dd]1444 * Ask through for a cloned connection to some service.
[f74392f]1445 *
[79ae36dd]1446 * @param mgmt Exchange management style.
1447 * @param exch Exchange for sending the message.
[007e6efa]1448 *
[79ae36dd]1449 * @return New session on success or NULL on error.
[f74392f]1450 *
1451 */
[79ae36dd]1452async_sess_t *async_connect_me(exch_mgmt_t mgmt, async_exch_t *exch)
1453{
1454 if (exch == NULL) {
1455 errno = ENOENT;
1456 return NULL;
1457 }
1458
1459 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1460 if (sess == NULL) {
1461 errno = ENOMEM;
1462 return NULL;
1463 }
1464
1465 ipc_call_t result;
1466
1467 amsg_t *msg = malloc(sizeof(amsg_t));
1468 if (msg == NULL) {
1469 free(sess);
1470 errno = ENOMEM;
1471 return NULL;
1472 }
1473
1474 msg->done = false;
1475 msg->dataptr = &result;
1476
1477 msg->wdata.to_event.inlist = false;
1478
1479 /*
1480 * We may sleep in the next method,
1481 * but it will use its own means
1482 */
1483 msg->wdata.active = true;
1484
1485 ipc_call_async_0(exch->phone, IPC_M_CONNECT_ME, msg,
1486 reply_received, true);
1487
1488 sysarg_t rc;
1489 async_wait_for((aid_t) msg, &rc);
1490
1491 if (rc != EOK) {
1492 errno = rc;
1493 free(sess);
1494 return NULL;
1495 }
1496
1497 int phone = (int) IPC_GET_ARG5(result);
1498
1499 if (phone < 0) {
1500 errno = phone;
1501 free(sess);
1502 return NULL;
1503 }
1504
1505 sess->mgmt = mgmt;
1506 sess->phone = phone;
1507 sess->arg1 = 0;
1508 sess->arg2 = 0;
1509 sess->arg3 = 0;
1510
1511 list_initialize(&sess->exch_list);
1512 fibril_mutex_initialize(&sess->mutex);
1513 atomic_set(&sess->refcnt, 0);
1514
1515 return sess;
1516}
1517
1518static int async_connect_me_to_internal(int phone, sysarg_t arg1, sysarg_t arg2,
1519 sysarg_t arg3, sysarg_t arg4)
[f74392f]1520{
[79ae36dd]1521 ipc_call_t result;
1522
1523 amsg_t *msg = malloc(sizeof(amsg_t));
1524 if (msg == NULL)
1525 return ENOENT;
1526
1527 msg->done = false;
1528 msg->dataptr = &result;
1529
1530 msg->wdata.to_event.inlist = false;
1531
1532 /*
1533 * We may sleep in the next method,
1534 * but it will use its own means
1535 */
1536 msg->wdata.active = true;
1537
1538 ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, arg4,
1539 msg, reply_received, true);
1540
1541 sysarg_t rc;
1542 async_wait_for((aid_t) msg, &rc);
[f74392f]1543
[007e6efa]1544 if (rc != EOK)
[f74392f]1545 return rc;
[007e6efa]1546
[79ae36dd]1547 return (int) IPC_GET_ARG5(result);
1548}
1549
1550/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
1551 *
1552 * Ask through for a new connection to some service.
1553 *
1554 * @param mgmt Exchange management style.
1555 * @param exch Exchange for sending the message.
1556 * @param arg1 User defined argument.
1557 * @param arg2 User defined argument.
1558 * @param arg3 User defined argument.
1559 *
1560 * @return New session on success or NULL on error.
1561 *
1562 */
1563async_sess_t *async_connect_me_to(exch_mgmt_t mgmt, async_exch_t *exch,
1564 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
1565{
1566 if (exch == NULL) {
1567 errno = ENOENT;
1568 return NULL;
1569 }
1570
1571 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1572 if (sess == NULL) {
1573 errno = ENOMEM;
1574 return NULL;
1575 }
1576
1577 int phone = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
1578 0);
1579
1580 if (phone < 0) {
1581 errno = phone;
1582 free(sess);
1583 return NULL;
1584 }
1585
1586 sess->mgmt = mgmt;
1587 sess->phone = phone;
1588 sess->arg1 = arg1;
1589 sess->arg2 = arg2;
1590 sess->arg3 = arg3;
1591
1592 list_initialize(&sess->exch_list);
1593 fibril_mutex_initialize(&sess->mutex);
1594 atomic_set(&sess->refcnt, 0);
1595
1596 return sess;
[f74392f]1597}
1598
1599/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
[007e6efa]1600 *
[f74392f]1601 * Ask through phone for a new connection to some service and block until
1602 * success.
1603 *
[79ae36dd]1604 * @param mgmt Exchange management style.
1605 * @param exch Exchange for sending the message.
1606 * @param arg1 User defined argument.
1607 * @param arg2 User defined argument.
1608 * @param arg3 User defined argument.
[007e6efa]1609 *
[79ae36dd]1610 * @return New session on success or NULL on error.
[f74392f]1611 *
1612 */
[79ae36dd]1613async_sess_t *async_connect_me_to_blocking(exch_mgmt_t mgmt, async_exch_t *exch,
1614 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
[f74392f]1615{
[79ae36dd]1616 if (exch == NULL) {
1617 errno = ENOENT;
1618 return NULL;
1619 }
[f74392f]1620
[79ae36dd]1621 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1622 if (sess == NULL) {
1623 errno = ENOMEM;
1624 return NULL;
1625 }
[007e6efa]1626
[79ae36dd]1627 int phone = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
1628 IPC_FLAG_BLOCKING);
1629
1630 if (phone < 0) {
1631 errno = phone;
1632 free(sess);
1633 return NULL;
1634 }
1635
1636 sess->mgmt = mgmt;
1637 sess->phone = phone;
1638 sess->arg1 = arg1;
1639 sess->arg2 = arg2;
1640 sess->arg3 = arg3;
1641
1642 list_initialize(&sess->exch_list);
1643 fibril_mutex_initialize(&sess->mutex);
1644 atomic_set(&sess->refcnt, 0);
1645
1646 return sess;
[f74392f]1647}
1648
[64d2b10]1649/** Connect to a task specified by id.
1650 *
1651 */
[79ae36dd]1652async_sess_t *async_connect_kbox(task_id_t id)
[64d2b10]1653{
[79ae36dd]1654 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1655 if (sess == NULL) {
1656 errno = ENOMEM;
1657 return NULL;
1658 }
1659
1660 int phone = ipc_connect_kbox(id);
1661 if (phone < 0) {
1662 errno = phone;
1663 free(sess);
1664 return NULL;
1665 }
1666
1667 sess->mgmt = EXCHANGE_ATOMIC;
1668 sess->phone = phone;
1669 sess->arg1 = 0;
1670 sess->arg2 = 0;
1671 sess->arg3 = 0;
1672
1673 list_initialize(&sess->exch_list);
1674 fibril_mutex_initialize(&sess->mutex);
1675 atomic_set(&sess->refcnt, 0);
1676
1677 return sess;
1678}
1679
1680static int async_hangup_internal(int phone)
1681{
1682 return ipc_hangup(phone);
[64d2b10]1683}
1684
1685/** Wrapper for ipc_hangup.
1686 *
[79ae36dd]1687 * @param sess Session to hung up.
[64d2b10]1688 *
1689 * @return Zero on success or a negative error code.
1690 *
1691 */
[79ae36dd]1692int async_hangup(async_sess_t *sess)
[64d2b10]1693{
[79ae36dd]1694 assert(sess);
1695
1696 if (atomic_get(&sess->refcnt) > 0)
1697 return EBUSY;
1698
1699 int rc = async_hangup_internal(sess->phone);
1700 if (rc == EOK)
1701 free(sess);
1702
1703 return rc;
[64d2b10]1704}
1705
1706/** Interrupt one thread of this task from waiting for IPC. */
1707void async_poke(void)
1708{
1709 ipc_poke();
1710}
1711
[79ae36dd]1712/** Start new exchange in a session.
1713 *
1714 * @param session Session.
1715 *
1716 * @return New exchange or NULL on error.
1717 *
1718 */
1719async_exch_t *async_exchange_begin(async_sess_t *sess)
1720{
1721 if (sess == NULL)
1722 return NULL;
1723
1724 async_exch_t *exch;
1725
1726 fibril_mutex_lock(&async_sess_mutex);
1727
1728 if (!list_empty(&sess->exch_list)) {
1729 /*
1730 * There are inactive exchanges in the session.
1731 */
1732 exch = (async_exch_t *)
1733 list_get_instance(sess->exch_list.next, async_exch_t, sess_link);
1734 list_remove(&exch->sess_link);
1735 list_remove(&exch->global_link);
1736 } else {
1737 /*
1738 * There are no available exchanges in the session.
1739 */
1740
1741 if ((sess->mgmt == EXCHANGE_ATOMIC) ||
1742 (sess->mgmt == EXCHANGE_SERIALIZE)) {
1743 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
1744 if (exch != NULL) {
1745 list_initialize(&exch->sess_link);
1746 list_initialize(&exch->global_link);
1747 exch->sess = sess;
1748 exch->phone = sess->phone;
1749 }
1750 } else { /* EXCHANGE_PARALLEL */
1751 /*
1752 * Make a one-time attempt to connect a new data phone.
1753 */
1754
1755 int phone;
1756
1757retry:
1758 phone = async_connect_me_to_internal(sess->phone, sess->arg1,
1759 sess->arg2, sess->arg3, 0);
1760 if (phone >= 0) {
1761 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
1762 if (exch != NULL) {
1763 list_initialize(&exch->sess_link);
1764 list_initialize(&exch->global_link);
1765 exch->sess = sess;
1766 exch->phone = phone;
1767 } else
1768 async_hangup_internal(phone);
1769 } else if (!list_empty(&inactive_exch_list)) {
1770 /*
1771 * We did not manage to connect a new phone. But we
1772 * can try to close some of the currently inactive
1773 * connections in other sessions and try again.
1774 */
1775 exch = (async_exch_t *)
1776 list_get_instance(inactive_exch_list.next, async_exch_t,
1777 global_link);
1778 list_remove(&exch->sess_link);
1779 list_remove(&exch->global_link);
1780 async_hangup_internal(exch->phone);
1781 free(exch);
1782 goto retry;
1783 } else {
1784 /*
1785 * Wait for a phone to become available.
1786 */
1787 fibril_condvar_wait(&avail_phone_cv, &async_sess_mutex);
1788 goto retry;
1789 }
1790 }
1791 }
1792
1793 fibril_mutex_unlock(&async_sess_mutex);
1794
1795 if (exch != NULL) {
1796 atomic_inc(&sess->refcnt);
1797
1798 if (sess->mgmt == EXCHANGE_SERIALIZE)
1799 fibril_mutex_lock(&sess->mutex);
1800 }
1801
1802 return exch;
1803}
1804
1805/** Finish an exchange.
1806 *
1807 * @param exch Exchange to finish.
1808 *
1809 */
1810void async_exchange_end(async_exch_t *exch)
1811{
1812 if (exch == NULL)
1813 return;
1814
1815 async_sess_t *sess = exch->sess;
1816
[1c6436a]1817 atomic_dec(&sess->refcnt);
1818
[79ae36dd]1819 if (sess->mgmt == EXCHANGE_SERIALIZE)
1820 fibril_mutex_unlock(&sess->mutex);
1821
1822 fibril_mutex_lock(&async_sess_mutex);
1823
1824 list_append(&exch->sess_link, &sess->exch_list);
1825 list_append(&exch->global_link, &inactive_exch_list);
1826 fibril_condvar_signal(&avail_phone_cv);
1827
1828 fibril_mutex_unlock(&async_sess_mutex);
1829}
1830
[47b7006]1831/** Wrapper for IPC_M_SHARE_IN calls using the async framework.
1832 *
[79ae36dd]1833 * @param exch Exchange for sending the message.
1834 * @param dst Destination address space area base.
1835 * @param size Size of the destination address space area.
1836 * @param arg User defined argument.
1837 * @param flags Storage for the received flags. Can be NULL.
[0da4e41]1838 *
[47b7006]1839 * @return Zero on success or a negative error code from errno.h.
[0da4e41]1840 *
1841 */
[79ae36dd]1842int async_share_in_start(async_exch_t *exch, void *dst, size_t size,
1843 sysarg_t arg, unsigned int *flags)
[0da4e41]1844{
[79ae36dd]1845 if (exch == NULL)
1846 return ENOENT;
1847
[0da4e41]1848 sysarg_t tmp_flags;
[79ae36dd]1849 int res = async_req_3_2(exch, IPC_M_SHARE_IN, (sysarg_t) dst,
[96b02eb9]1850 (sysarg_t) size, arg, NULL, &tmp_flags);
[47b7006]1851
[0da4e41]1852 if (flags)
[47b7006]1853 *flags = (unsigned int) tmp_flags;
1854
[0da4e41]1855 return res;
1856}
1857
1858/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
1859 *
[47b7006]1860 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN
1861 * calls so that the user doesn't have to remember the meaning of each IPC
1862 * argument.
[0da4e41]1863 *
1864 * So far, this wrapper is to be used from within a connection fibril.
1865 *
[47b7006]1866 * @param callid Storage for the hash of the IPC_M_SHARE_IN call.
1867 * @param size Destination address space area size.
1868 *
1869 * @return True on success, false on failure.
[0da4e41]1870 *
1871 */
[47b7006]1872bool async_share_in_receive(ipc_callid_t *callid, size_t *size)
[0da4e41]1873{
1874 assert(callid);
1875 assert(size);
[47b7006]1876
1877 ipc_call_t data;
[0da4e41]1878 *callid = async_get_call(&data);
[47b7006]1879
[228e490]1880 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
[47b7006]1881 return false;
1882
[0da4e41]1883 *size = (size_t) IPC_GET_ARG2(data);
[47b7006]1884 return true;
[0da4e41]1885}
1886
1887/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
1888 *
[47b7006]1889 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
1890 * calls so that the user doesn't have to remember the meaning of each IPC
1891 * argument.
[0da4e41]1892 *
[47b7006]1893 * @param callid Hash of the IPC_M_DATA_READ call to answer.
1894 * @param src Source address space base.
1895 * @param flags Flags to be used for sharing. Bits can be only cleared.
1896 *
1897 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1898 *
1899 */
[47b7006]1900int async_share_in_finalize(ipc_callid_t callid, void *src, unsigned int flags)
[0da4e41]1901{
1902 return ipc_share_in_finalize(callid, src, flags);
1903}
1904
[47b7006]1905/** Wrapper for IPC_M_SHARE_OUT calls using the async framework.
[0da4e41]1906 *
[79ae36dd]1907 * @param exch Exchange for sending the message.
1908 * @param src Source address space area base address.
1909 * @param flags Flags to be used for sharing. Bits can be only cleared.
[47b7006]1910 *
1911 * @return Zero on success or a negative error code from errno.h.
[0da4e41]1912 *
1913 */
[79ae36dd]1914int async_share_out_start(async_exch_t *exch, void *src, unsigned int flags)
[0da4e41]1915{
[79ae36dd]1916 if (exch == NULL)
1917 return ENOENT;
1918
1919 return async_req_3_0(exch, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
[96b02eb9]1920 (sysarg_t) flags);
[0da4e41]1921}
1922
1923/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
1924 *
[47b7006]1925 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT
1926 * calls so that the user doesn't have to remember the meaning of each IPC
1927 * argument.
[0da4e41]1928 *
1929 * So far, this wrapper is to be used from within a connection fibril.
1930 *
[47b7006]1931 * @param callid Storage for the hash of the IPC_M_SHARE_OUT call.
1932 * @param size Storage for the source address space area size.
1933 * @param flags Storage for the sharing flags.
1934 *
1935 * @return True on success, false on failure.
[0da4e41]1936 *
1937 */
[47b7006]1938bool async_share_out_receive(ipc_callid_t *callid, size_t *size, unsigned int *flags)
[0da4e41]1939{
1940 assert(callid);
1941 assert(size);
1942 assert(flags);
[47b7006]1943
1944 ipc_call_t data;
[0da4e41]1945 *callid = async_get_call(&data);
[47b7006]1946
[228e490]1947 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
[47b7006]1948 return false;
1949
[0da4e41]1950 *size = (size_t) IPC_GET_ARG2(data);
[47b7006]1951 *flags = (unsigned int) IPC_GET_ARG3(data);
1952 return true;
[0da4e41]1953}
1954
1955/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
1956 *
[47b7006]1957 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
1958 * calls so that the user doesn't have to remember the meaning of each IPC
1959 * argument.
[0da4e41]1960 *
[47b7006]1961 * @param callid Hash of the IPC_M_DATA_WRITE call to answer.
1962 * @param dst Destination address space area base address.
1963 *
1964 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1965 *
1966 */
1967int async_share_out_finalize(ipc_callid_t callid, void *dst)
1968{
1969 return ipc_share_out_finalize(callid, dst);
1970}
1971
[8bf1eeb]1972/** Start IPC_M_DATA_READ using the async framework.
1973 *
[79ae36dd]1974 * @param exch Exchange for sending the message.
1975 * @param dst Address of the beginning of the destination buffer.
1976 * @param size Size of the destination buffer (in bytes).
[8bf1eeb]1977 * @param dataptr Storage of call data (arg 2 holds actual data size).
[79ae36dd]1978 *
[8bf1eeb]1979 * @return Hash of the sent message or 0 on error.
[79ae36dd]1980 *
[8bf1eeb]1981 */
[79ae36dd]1982aid_t async_data_read(async_exch_t *exch, void *dst, size_t size,
1983 ipc_call_t *dataptr)
[8bf1eeb]1984{
[79ae36dd]1985 return async_send_2(exch, IPC_M_DATA_READ, (sysarg_t) dst,
[8bf1eeb]1986 (sysarg_t) size, dataptr);
1987}
1988
[47b7006]1989/** Wrapper for IPC_M_DATA_READ calls using the async framework.
[0da4e41]1990 *
[79ae36dd]1991 * @param exch Exchange for sending the message.
1992 * @param dst Address of the beginning of the destination buffer.
1993 * @param size Size of the destination buffer.
[47b7006]1994 *
1995 * @return Zero on success or a negative error code from errno.h.
[0da4e41]1996 *
1997 */
[79ae36dd]1998int async_data_read_start(async_exch_t *exch, void *dst, size_t size)
[0da4e41]1999{
[79ae36dd]2000 if (exch == NULL)
2001 return ENOENT;
2002
2003 return async_req_2_0(exch, IPC_M_DATA_READ, (sysarg_t) dst,
2004 (sysarg_t) size);
[0da4e41]2005}
2006
2007/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
2008 *
[47b7006]2009 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
2010 * calls so that the user doesn't have to remember the meaning of each IPC
2011 * argument.
[0da4e41]2012 *
2013 * So far, this wrapper is to be used from within a connection fibril.
2014 *
[47b7006]2015 * @param callid Storage for the hash of the IPC_M_DATA_READ.
2016 * @param size Storage for the maximum size. Can be NULL.
2017 *
2018 * @return True on success, false on failure.
[0da4e41]2019 *
2020 */
[47b7006]2021bool async_data_read_receive(ipc_callid_t *callid, size_t *size)
[0da4e41]2022{
2023 assert(callid);
[47b7006]2024
2025 ipc_call_t data;
[0da4e41]2026 *callid = async_get_call(&data);
[47b7006]2027
[228e490]2028 if (IPC_GET_IMETHOD(data) != IPC_M_DATA_READ)
[47b7006]2029 return false;
2030
[0da4e41]2031 if (size)
2032 *size = (size_t) IPC_GET_ARG2(data);
[47b7006]2033
2034 return true;
[0da4e41]2035}
2036
2037/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
2038 *
[47b7006]2039 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
2040 * calls so that the user doesn't have to remember the meaning of each IPC
2041 * argument.
[0da4e41]2042 *
[47b7006]2043 * @param callid Hash of the IPC_M_DATA_READ call to answer.
2044 * @param src Source address for the IPC_M_DATA_READ call.
2045 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
2046 * the maximum size announced by the sender.
2047 *
2048 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]2049 *
2050 */
2051int async_data_read_finalize(ipc_callid_t callid, const void *src, size_t size)
2052{
2053 return ipc_data_read_finalize(callid, src, size);
2054}
2055
[b4cbef1]2056/** Wrapper for forwarding any read request
2057 *
2058 */
[79ae36dd]2059int async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
2060 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
2061 ipc_call_t *dataptr)
[b4cbef1]2062{
[79ae36dd]2063 if (exch == NULL)
2064 return ENOENT;
2065
[b4cbef1]2066 ipc_callid_t callid;
2067 if (!async_data_read_receive(&callid, NULL)) {
2068 ipc_answer_0(callid, EINVAL);
2069 return EINVAL;
2070 }
2071
[79ae36dd]2072 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]2073 dataptr);
2074 if (msg == 0) {
2075 ipc_answer_0(callid, EINVAL);
2076 return EINVAL;
2077 }
2078
[79ae36dd]2079 int retval = ipc_forward_fast(callid, exch->phone, 0, 0, 0,
[b4cbef1]2080 IPC_FF_ROUTE_FROM_ME);
2081 if (retval != EOK) {
[a281fc82]2082 async_wait_for(msg, NULL);
[b4cbef1]2083 ipc_answer_0(callid, retval);
2084 return retval;
2085 }
2086
[96b02eb9]2087 sysarg_t rc;
[b4cbef1]2088 async_wait_for(msg, &rc);
2089
2090 return (int) rc;
2091}
2092
[47b7006]2093/** Wrapper for IPC_M_DATA_WRITE calls using the async framework.
[0da4e41]2094 *
[79ae36dd]2095 * @param exch Exchange for sending the message.
2096 * @param src Address of the beginning of the source buffer.
2097 * @param size Size of the source buffer.
[b4cbef1]2098 *
2099 * @return Zero on success or a negative error code from errno.h.
[0da4e41]2100 *
2101 */
[79ae36dd]2102int async_data_write_start(async_exch_t *exch, const void *src, size_t size)
[0da4e41]2103{
[79ae36dd]2104 if (exch == NULL)
2105 return ENOENT;
2106
2107 return async_req_2_0(exch, IPC_M_DATA_WRITE, (sysarg_t) src,
2108 (sysarg_t) size);
[0da4e41]2109}
2110
2111/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
2112 *
[47b7006]2113 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
2114 * calls so that the user doesn't have to remember the meaning of each IPC
2115 * argument.
[0da4e41]2116 *
2117 * So far, this wrapper is to be used from within a connection fibril.
2118 *
[47b7006]2119 * @param callid Storage for the hash of the IPC_M_DATA_WRITE.
2120 * @param size Storage for the suggested size. May be NULL.
[b4cbef1]2121 *
[47b7006]2122 * @return True on success, false on failure.
[0da4e41]2123 *
2124 */
[47b7006]2125bool async_data_write_receive(ipc_callid_t *callid, size_t *size)
[0da4e41]2126{
2127 assert(callid);
[b4cbef1]2128
[47b7006]2129 ipc_call_t data;
[0da4e41]2130 *callid = async_get_call(&data);
[47b7006]2131
[228e490]2132 if (IPC_GET_IMETHOD(data) != IPC_M_DATA_WRITE)
[47b7006]2133 return false;
[b4cbef1]2134
[0da4e41]2135 if (size)
2136 *size = (size_t) IPC_GET_ARG2(data);
[b4cbef1]2137
[47b7006]2138 return true;
[0da4e41]2139}
2140
2141/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
2142 *
[47b7006]2143 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
2144 * calls so that the user doesn't have to remember the meaning of each IPC
2145 * argument.
[0da4e41]2146 *
[b4cbef1]2147 * @param callid Hash of the IPC_M_DATA_WRITE call to answer.
2148 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
2149 * @param size Final size for the IPC_M_DATA_WRITE call.
2150 *
2151 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]2152 *
2153 */
2154int async_data_write_finalize(ipc_callid_t callid, void *dst, size_t size)
2155{
2156 return ipc_data_write_finalize(callid, dst, size);
2157}
2158
[eda925a]2159/** Wrapper for receiving binary data or strings
[8aa42e3]2160 *
2161 * This wrapper only makes it more comfortable to use async_data_write_*
[eda925a]2162 * functions to receive binary data or strings.
[8aa42e3]2163 *
[472c09d]2164 * @param data Pointer to data pointer (which should be later disposed
2165 * by free()). If the operation fails, the pointer is not
2166 * touched.
[eda925a]2167 * @param nullterm If true then the received data is always zero terminated.
2168 * This also causes to allocate one extra byte beyond the
2169 * raw transmitted data.
[b4cbef1]2170 * @param min_size Minimum size (in bytes) of the data to receive.
[472c09d]2171 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
2172 * no limit.
[eda925a]2173 * @param granulariy If non-zero then the size of the received data has to
[472c09d]2174 * be divisible by this value.
2175 * @param received If not NULL, the size of the received data is stored here.
[8aa42e3]2176 *
2177 * @return Zero on success or a value from @ref errno.h on failure.
2178 *
2179 */
[eda925a]2180int async_data_write_accept(void **data, const bool nullterm,
2181 const size_t min_size, const size_t max_size, const size_t granularity,
2182 size_t *received)
[8aa42e3]2183{
[79ae36dd]2184 assert(data);
2185
[8aa42e3]2186 ipc_callid_t callid;
2187 size_t size;
2188 if (!async_data_write_receive(&callid, &size)) {
2189 ipc_answer_0(callid, EINVAL);
2190 return EINVAL;
2191 }
2192
[b4cbef1]2193 if (size < min_size) {
2194 ipc_answer_0(callid, EINVAL);
2195 return EINVAL;
2196 }
2197
[8aa42e3]2198 if ((max_size > 0) && (size > max_size)) {
2199 ipc_answer_0(callid, EINVAL);
2200 return EINVAL;
2201 }
2202
[472c09d]2203 if ((granularity > 0) && ((size % granularity) != 0)) {
2204 ipc_answer_0(callid, EINVAL);
2205 return EINVAL;
2206 }
2207
[eda925a]2208 void *_data;
2209
2210 if (nullterm)
2211 _data = malloc(size + 1);
2212 else
2213 _data = malloc(size);
2214
[472c09d]2215 if (_data == NULL) {
[8aa42e3]2216 ipc_answer_0(callid, ENOMEM);
2217 return ENOMEM;
2218 }
2219
[472c09d]2220 int rc = async_data_write_finalize(callid, _data, size);
[8aa42e3]2221 if (rc != EOK) {
[472c09d]2222 free(_data);
[8aa42e3]2223 return rc;
2224 }
2225
[eda925a]2226 if (nullterm)
2227 ((char *) _data)[size] = 0;
[8aa42e3]2228
[eda925a]2229 *data = _data;
[472c09d]2230 if (received != NULL)
2231 *received = size;
2232
[8aa42e3]2233 return EOK;
2234}
2235
[b4cbef1]2236/** Wrapper for voiding any data that is about to be received
2237 *
2238 * This wrapper can be used to void any pending data
2239 *
2240 * @param retval Error value from @ref errno.h to be returned to the caller.
2241 *
2242 */
[47b7006]2243void async_data_write_void(sysarg_t retval)
[b4cbef1]2244{
2245 ipc_callid_t callid;
2246 async_data_write_receive(&callid, NULL);
2247 ipc_answer_0(callid, retval);
2248}
2249
2250/** Wrapper for forwarding any data that is about to be received
2251 *
2252 */
[79ae36dd]2253int async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
2254 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
2255 ipc_call_t *dataptr)
[b4cbef1]2256{
[79ae36dd]2257 if (exch == NULL)
2258 return ENOENT;
2259
[b4cbef1]2260 ipc_callid_t callid;
2261 if (!async_data_write_receive(&callid, NULL)) {
2262 ipc_answer_0(callid, EINVAL);
2263 return EINVAL;
2264 }
2265
[79ae36dd]2266 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]2267 dataptr);
2268 if (msg == 0) {
2269 ipc_answer_0(callid, EINVAL);
2270 return EINVAL;
2271 }
2272
[79ae36dd]2273 int retval = ipc_forward_fast(callid, exch->phone, 0, 0, 0,
[b4cbef1]2274 IPC_FF_ROUTE_FROM_ME);
2275 if (retval != EOK) {
[a281fc82]2276 async_wait_for(msg, NULL);
[b4cbef1]2277 ipc_answer_0(callid, retval);
2278 return retval;
2279 }
2280
[96b02eb9]2281 sysarg_t rc;
[b4cbef1]2282 async_wait_for(msg, &rc);
2283
2284 return (int) rc;
2285}
2286
[79ae36dd]2287/** Wrapper for sending an exchange over different exchange for cloning
2288 *
2289 * @param exch Exchange to be used for sending.
2290 * @param clone_exch Exchange to be cloned.
2291 *
2292 */
2293int async_exchange_clone(async_exch_t *exch, async_exch_t *clone_exch)
2294{
2295 return async_req_1_0(exch, IPC_M_CONNECTION_CLONE, clone_exch->phone);
2296}
2297
2298/** Wrapper for receiving the IPC_M_CONNECTION_CLONE calls.
2299 *
2300 * If the current call is IPC_M_CONNECTION_CLONE then a new
2301 * async session is created for the accepted phone.
2302 *
2303 * @param mgmt Exchange management style.
2304 *
2305 * @return New async session or NULL on failure.
2306 *
2307 */
2308async_sess_t *async_clone_receive(exch_mgmt_t mgmt)
2309{
2310 /* Accept the phone */
2311 ipc_call_t call;
2312 ipc_callid_t callid = async_get_call(&call);
2313 int phone = (int) IPC_GET_ARG1(call);
2314
2315 if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECTION_CLONE) ||
2316 (phone < 0)) {
2317 async_answer_0(callid, EINVAL);
2318 return NULL;
2319 }
2320
2321 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2322 if (sess == NULL) {
2323 async_answer_0(callid, ENOMEM);
2324 return NULL;
2325 }
2326
2327 sess->mgmt = mgmt;
2328 sess->phone = phone;
2329 sess->arg1 = 0;
2330 sess->arg2 = 0;
2331 sess->arg3 = 0;
2332
2333 list_initialize(&sess->exch_list);
2334 fibril_mutex_initialize(&sess->mutex);
2335 atomic_set(&sess->refcnt, 0);
2336
2337 /* Acknowledge the cloned phone */
2338 async_answer_0(callid, EOK);
2339
2340 return sess;
2341}
2342
2343/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
2344 *
2345 * If the current call is IPC_M_CONNECT_TO_ME then a new
2346 * async session is created for the accepted phone.
2347 *
2348 * @param mgmt Exchange management style.
2349 *
[8869f7b]2350 * @return New async session.
2351 * @return NULL on failure.
[79ae36dd]2352 *
2353 */
2354async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
2355{
2356 /* Accept the phone */
2357 ipc_call_t call;
2358 ipc_callid_t callid = async_get_call(&call);
2359 int phone = (int) IPC_GET_ARG5(call);
2360
2361 if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) ||
2362 (phone < 0)) {
2363 async_answer_0(callid, EINVAL);
2364 return NULL;
2365 }
2366
2367 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2368 if (sess == NULL) {
2369 async_answer_0(callid, ENOMEM);
2370 return NULL;
2371 }
2372
2373 sess->mgmt = mgmt;
2374 sess->phone = phone;
2375 sess->arg1 = 0;
2376 sess->arg2 = 0;
2377 sess->arg3 = 0;
2378
2379 list_initialize(&sess->exch_list);
2380 fibril_mutex_initialize(&sess->mutex);
2381 atomic_set(&sess->refcnt, 0);
2382
2383 /* Acknowledge the connected phone */
2384 async_answer_0(callid, EOK);
2385
2386 return sess;
2387}
2388
[8869f7b]2389/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
2390 *
2391 * If the call is IPC_M_CONNECT_TO_ME then a new
2392 * async session is created. However, the phone is
2393 * not accepted automatically.
2394 *
2395 * @param mgmt Exchange management style.
2396 * @param call Call data.
2397 *
2398 * @return New async session.
2399 * @return NULL on failure.
2400 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
2401 *
2402 */
2403async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
2404{
2405 int phone = (int) IPC_GET_ARG5(*call);
2406
2407 if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) ||
2408 (phone < 0))
2409 return NULL;
2410
2411 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2412 if (sess == NULL)
2413 return NULL;
2414
2415 sess->mgmt = mgmt;
2416 sess->phone = phone;
2417 sess->arg1 = 0;
2418 sess->arg2 = 0;
2419 sess->arg3 = 0;
2420
2421 list_initialize(&sess->exch_list);
2422 fibril_mutex_initialize(&sess->mutex);
2423 atomic_set(&sess->refcnt, 0);
2424
2425 return sess;
2426}
2427
[a46da63]2428/** @}
[b2951e2]2429 */
Note: See TracBrowser for help on using the repository browser.