source: mainline/uspace/lib/c/generic/async.c@ df02460

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since df02460 was 2c4aa39, checked in by Jakub Jermar <jakub@…>, 14 years ago

Add async framework wrappers for IPC_M_STATE_CHANGE_AUTHORIZE.

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