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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f1380b7 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 79.6 KB
RevLine 
[06502f7d]1/*
[df4ed85]2 * Copyright (c) 2006 Ondrej Palkovsky
[06502f7d]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[b2951e2]27 */
28
[a46da63]29/** @addtogroup libc
[b2951e2]30 * @{
31 */
32/** @file
[c07544d3]33 */
[06502f7d]34
[80649a91]35/**
36 * Asynchronous library
37 *
[c07544d3]38 * The aim of this library is to provide a facility for writing programs which
39 * utilize the asynchronous nature of HelenOS IPC, yet using a normal way of
40 * programming.
[80649a91]41 *
[79ae36dd]42 * You should be able to write very simple multithreaded programs. The async
43 * framework will automatically take care of most of the synchronization
44 * problems.
[80649a91]45 *
[9591265]46 * Example of use (pseudo C):
[c07544d3]47 *
[80649a91]48 * 1) Multithreaded client application
[9591265]49 *
[c07544d3]50 * fibril_create(fibril1, ...);
51 * fibril_create(fibril2, ...);
52 * ...
53 *
54 * int fibril1(void *arg)
55 * {
[79ae36dd]56 * conn = async_connect_me_to(...);
57 *
58 * exch = async_exchange_begin(conn);
59 * c1 = async_send(exch);
60 * async_exchange_end(exch);
61 *
62 * exch = async_exchange_begin(conn);
63 * c2 = async_send(exch);
64 * async_exchange_end(exch);
65 *
[c07544d3]66 * async_wait_for(c1);
67 * async_wait_for(c2);
68 * ...
69 * }
[80649a91]70 *
71 *
72 * 2) Multithreaded server application
73 *
[c07544d3]74 * main()
75 * {
76 * async_manager();
77 * }
78 *
[01c3bb4]79 * port_handler(ichandle, *icall)
[c07544d3]80 * {
81 * if (want_refuse) {
[01c3bb4]82 * async_answer_0(ichandle, ELIMIT);
[c07544d3]83 * return;
84 * }
[01c3bb4]85 * async_answer_0(ichandle, EOK);
[80649a91]86 *
[01c3bb4]87 * chandle = async_get_call(&call);
88 * somehow_handle_the_call(chandle, call);
89 * async_answer_2(chandle, 1, 2, 3);
[53ca318]90 *
[01c3bb4]91 * chandle = async_get_call(&call);
[c07544d3]92 * ...
93 * }
[a2cd194]94 *
[80649a91]95 */
[9591265]96
[64d2b10]97#define LIBC_ASYNC_C_
98#include <ipc/ipc.h>
[80649a91]99#include <async.h>
[b76a7329]100#include "private/async.h"
[64d2b10]101#undef LIBC_ASYNC_C_
102
[8820544]103#include <ipc/irq.h>
104#include <ipc/event.h>
[64d2b10]105#include <futex.h>
[bc1f1c2]106#include <fibril.h>
[d9c8c81]107#include <adt/hash_table.h>
[853802e]108#include <adt/hash.h>
[d9c8c81]109#include <adt/list.h>
[80649a91]110#include <assert.h>
111#include <errno.h>
[daa90e8]112#include <sys/time.h>
[c0699467]113#include <libarch/barrier.h>
[3e6a98c5]114#include <stdbool.h>
[38d150e]115#include <stdlib.h>
[79ae36dd]116#include <mem.h>
117#include <stdlib.h>
[e2ab36f1]118#include <macros.h>
[101516d]119#include <as.h>
[ae6021d]120#include <abi/mm/as.h>
[d7978525]121#include "private/libc.h"
[80649a91]122
[5da7199]123/** Session data */
124struct async_sess {
125 /** List of inactive exchanges */
126 list_t exch_list;
[a35b458]127
[566992e1]128 /** Session interface */
129 iface_t iface;
[a35b458]130
[5da7199]131 /** Exchange management style */
132 exch_mgmt_t mgmt;
[a35b458]133
[5da7199]134 /** Session identification */
135 int phone;
[a35b458]136
[5da7199]137 /** First clone connection argument */
138 sysarg_t arg1;
[a35b458]139
[5da7199]140 /** Second clone connection argument */
141 sysarg_t arg2;
[a35b458]142
[5da7199]143 /** Third clone connection argument */
144 sysarg_t arg3;
[a35b458]145
[5da7199]146 /** Exchange mutex */
147 fibril_mutex_t mutex;
[a35b458]148
[5da7199]149 /** Number of opened exchanges */
150 atomic_t refcnt;
[a35b458]151
[5da7199]152 /** Mutex for stateful connections */
153 fibril_mutex_t remote_state_mtx;
[a35b458]154
[5da7199]155 /** Data for stateful connections */
156 void *remote_state_data;
157};
158
159/** Exchange data */
160struct async_exch {
161 /** Link into list of inactive exchanges */
162 link_t sess_link;
[a35b458]163
[5da7199]164 /** Link into global list of inactive exchanges */
165 link_t global_link;
[a35b458]166
[5da7199]167 /** Session pointer */
168 async_sess_t *sess;
[a35b458]169
[5da7199]170 /** Exchange identification */
171 int phone;
172};
173
[79ae36dd]174/** Async framework global futex */
[927a181e]175futex_t async_futex = FUTEX_INITIALIZER;
[80649a91]176
[8619f25]177/** Number of threads waiting for IPC in the kernel. */
178atomic_t threads_in_ipc_wait = { 0 };
179
[79ae36dd]180/** Naming service session */
181async_sess_t *session_ns;
[01ff41c]182
[79ae36dd]183/** Call data */
[80649a91]184typedef struct {
185 link_t link;
[a35b458]186
[01c3bb4]187 cap_handle_t chandle;
[80649a91]188 ipc_call_t call;
189} msg_t;
190
[5da7199]191/** Message data */
192typedef struct {
193 awaiter_t wdata;
[a35b458]194
[5da7199]195 /** If reply was received. */
196 bool done;
[a35b458]197
[47c9a8c]198 /** If the message / reply should be discarded on arrival. */
199 bool forget;
[a35b458]200
[47c9a8c]201 /** If already destroyed. */
202 bool destroyed;
[a35b458]203
[5da7199]204 /** Pointer to where the answer data is stored. */
205 ipc_call_t *dataptr;
[a35b458]206
[b7fd2a0]207 errno_t retval;
[5da7199]208} amsg_t;
209
[79ae36dd]210/* Client connection data */
[c80fdd0]211typedef struct {
[062d900]212 ht_link_t link;
[a35b458]213
[649f087]214 task_id_t in_task_id;
[79ae36dd]215 atomic_t refcnt;
[c80fdd0]216 void *data;
217} client_t;
218
[79ae36dd]219/* Server connection data */
[80649a91]220typedef struct {
[49d072e]221 awaiter_t wdata;
[a35b458]222
[e70bfa5]223 /** Hash table link. */
[062d900]224 ht_link_t link;
[a35b458]225
[e2ab36f1]226 /** Incoming client task ID. */
227 task_id_t in_task_id;
[a35b458]228
[e70bfa5]229 /** Incoming phone hash. */
[96b02eb9]230 sysarg_t in_phone_hash;
[a35b458]231
[23882034]232 /** Link to the client tracking structure. */
233 client_t *client;
[a35b458]234
[e70bfa5]235 /** Messages that should be delivered to this fibril. */
[b72efe8]236 list_t msg_queue;
[a35b458]237
[e70bfa5]238 /** Identification of the opening call. */
[01c3bb4]239 cap_handle_t chandle;
[a35b458]240
[e70bfa5]241 /** Call data of the opening call. */
[80649a91]242 ipc_call_t call;
[a35b458]243
[e70bfa5]244 /** Identification of the closing call. */
[01c3bb4]245 cap_handle_t close_chandle;
[a35b458]246
[e70bfa5]247 /** Fibril function that will be used to handle the connection. */
[b688fd8]248 async_port_handler_t handler;
[a35b458]249
[b688fd8]250 /** Client data */
251 void *data;
[80649a91]252} connection_t;
253
[566992e1]254/** Interface data */
255typedef struct {
256 ht_link_t link;
[a35b458]257
[566992e1]258 /** Interface ID */
259 iface_t iface;
[a35b458]260
[566992e1]261 /** Futex protecting the hash table */
262 futex_t futex;
[a35b458]263
[566992e1]264 /** Interface ports */
265 hash_table_t port_hash_table;
[a35b458]266
[566992e1]267 /** Next available port ID */
268 port_id_t port_id_avail;
269} interface_t;
270
271/* Port data */
272typedef struct {
273 ht_link_t link;
[a35b458]274
[566992e1]275 /** Port ID */
276 port_id_t id;
[a35b458]277
[566992e1]278 /** Port connection handler */
279 async_port_handler_t handler;
[a35b458]280
[566992e1]281 /** Client data */
282 void *data;
283} port_t;
284
[8820544]285/* Notification data */
286typedef struct {
287 ht_link_t link;
[a35b458]288
[8820544]289 /** Notification method */
290 sysarg_t imethod;
[a35b458]291
[8820544]292 /** Notification handler */
293 async_notification_handler_t handler;
[a35b458]294
[8820544]295 /** Notification data */
296 void *data;
297} notification_t;
298
[bc1f1c2]299/** Identifier of the incoming connection handled by the current fibril. */
[79ae36dd]300static fibril_local connection_t *fibril_connection;
[e70bfa5]301
[47c9a8c]302static void to_event_initialize(to_event_t *to)
303{
[aeeddeb]304 struct timeval tv = { 0, 0 };
[a35b458]305
[47c9a8c]306 to->inlist = false;
307 to->occurred = false;
308 link_initialize(&to->link);
309 to->expires = tv;
310}
311
312static void wu_event_initialize(wu_event_t *wu)
313{
314 wu->inlist = false;
315 link_initialize(&wu->link);
316}
317
318void awaiter_initialize(awaiter_t *aw)
319{
320 aw->fid = 0;
321 aw->active = false;
322 to_event_initialize(&aw->to_event);
323 wu_event_initialize(&aw->wu_event);
324}
325
326static amsg_t *amsg_create(void)
327{
[57dea62]328 amsg_t *msg = malloc(sizeof(amsg_t));
[47c9a8c]329 if (msg) {
330 msg->done = false;
331 msg->forget = false;
332 msg->destroyed = false;
333 msg->dataptr = NULL;
[25a179e]334 msg->retval = EINVAL;
[47c9a8c]335 awaiter_initialize(&msg->wdata);
336 }
[a35b458]337
[47c9a8c]338 return msg;
339}
340
341static void amsg_destroy(amsg_t *msg)
342{
343 assert(!msg->destroyed);
344 msg->destroyed = true;
345 free(msg);
346}
347
[46eec3b]348static void *default_client_data_constructor(void)
349{
350 return NULL;
351}
352
353static void default_client_data_destructor(void *data)
354{
355}
356
357static async_client_data_ctor_t async_client_data_create =
358 default_client_data_constructor;
359static async_client_data_dtor_t async_client_data_destroy =
360 default_client_data_destructor;
361
362void async_set_client_data_constructor(async_client_data_ctor_t ctor)
363{
[f302586]364 assert(async_client_data_create == default_client_data_constructor);
[46eec3b]365 async_client_data_create = ctor;
366}
367
368void async_set_client_data_destructor(async_client_data_dtor_t dtor)
369{
[f302586]370 assert(async_client_data_destroy == default_client_data_destructor);
[46eec3b]371 async_client_data_destroy = dtor;
372}
373
[b688fd8]374/** Default fallback fibril function.
[47b7006]375 *
[01c3bb4]376 * This fallback fibril function gets called on incomming connections that do
377 * not have a specific handler defined.
[47b7006]378 *
[01c3bb4]379 * @param chandle Handle of the incoming call.
380 * @param call Data of the incoming call.
381 * @param arg Local argument
[47b7006]382 *
383 */
[01c3bb4]384static void default_fallback_port_handler(cap_handle_t chandle,
385 ipc_call_t *call, void *arg)
[47b7006]386{
[01c3bb4]387 ipc_answer_0(chandle, ENOENT);
[47b7006]388}
[36c9234]389
[b688fd8]390static async_port_handler_t fallback_port_handler =
391 default_fallback_port_handler;
392static void *fallback_port_data = NULL;
[da0c91e7]393
[566992e1]394static hash_table_t interface_hash_table;
395
396static size_t interface_key_hash(void *key)
397{
398 iface_t iface = *(iface_t *) key;
399 return iface;
400}
401
402static size_t interface_hash(const ht_link_t *item)
403{
404 interface_t *interface = hash_table_get_inst(item, interface_t, link);
405 return interface_key_hash(&interface->iface);
406}
407
408static bool interface_key_equal(void *key, const ht_link_t *item)
409{
410 iface_t iface = *(iface_t *) key;
411 interface_t *interface = hash_table_get_inst(item, interface_t, link);
412 return iface == interface->iface;
413}
414
415/** Operations for the port hash table. */
416static hash_table_ops_t interface_hash_table_ops = {
417 .hash = interface_hash,
418 .key_hash = interface_key_hash,
419 .key_equal = interface_key_equal,
420 .equal = NULL,
421 .remove_callback = NULL
422};
423
424static size_t port_key_hash(void *key)
425{
426 port_id_t port_id = *(port_id_t *) key;
427 return port_id;
428}
429
430static size_t port_hash(const ht_link_t *item)
431{
432 port_t *port = hash_table_get_inst(item, port_t, link);
433 return port_key_hash(&port->id);
434}
435
436static bool port_key_equal(void *key, const ht_link_t *item)
437{
438 port_id_t port_id = *(port_id_t *) key;
439 port_t *port = hash_table_get_inst(item, port_t, link);
440 return port_id == port->id;
441}
442
443/** Operations for the port hash table. */
444static hash_table_ops_t port_hash_table_ops = {
445 .hash = port_hash,
446 .key_hash = port_key_hash,
447 .key_equal = port_key_equal,
448 .equal = NULL,
449 .remove_callback = NULL
450};
451
452static interface_t *async_new_interface(iface_t iface)
453{
454 interface_t *interface =
455 (interface_t *) malloc(sizeof(interface_t));
456 if (!interface)
457 return NULL;
[a35b458]458
[566992e1]459 bool ret = hash_table_create(&interface->port_hash_table, 0, 0,
460 &port_hash_table_ops);
461 if (!ret) {
462 free(interface);
463 return NULL;
464 }
[a35b458]465
[566992e1]466 interface->iface = iface;
467 futex_initialize(&interface->futex, 1);
468 interface->port_id_avail = 0;
[a35b458]469
[566992e1]470 hash_table_insert(&interface_hash_table, &interface->link);
[a35b458]471
[566992e1]472 return interface;
473}
474
475static port_t *async_new_port(interface_t *interface,
476 async_port_handler_t handler, void *data)
477{
478 port_t *port = (port_t *) malloc(sizeof(port_t));
479 if (!port)
480 return NULL;
[a35b458]481
[566992e1]482 futex_down(&interface->futex);
[a35b458]483
[566992e1]484 port_id_t id = interface->port_id_avail;
485 interface->port_id_avail++;
[a35b458]486
[566992e1]487 port->id = id;
488 port->handler = handler;
489 port->data = data;
[a35b458]490
[566992e1]491 hash_table_insert(&interface->port_hash_table, &port->link);
[a35b458]492
[566992e1]493 futex_up(&interface->futex);
[a35b458]494
[566992e1]495 return port;
496}
497
[79ae36dd]498/** Mutex protecting inactive_exch_list and avail_phone_cv.
499 *
500 */
501static FIBRIL_MUTEX_INITIALIZE(async_sess_mutex);
502
503/** List of all currently inactive exchanges.
504 *
505 */
506static LIST_INITIALIZE(inactive_exch_list);
507
508/** Condition variable to wait for a phone to become available.
509 *
510 */
511static FIBRIL_CONDVAR_INITIALIZE(avail_phone_cv);
512
[b7fd2a0]513errno_t async_create_port(iface_t iface, async_port_handler_t handler,
[566992e1]514 void *data, port_id_t *port_id)
515{
516 if ((iface & IFACE_MOD_MASK) == IFACE_MOD_CALLBACK)
517 return EINVAL;
[a35b458]518
[566992e1]519 interface_t *interface;
[a35b458]520
[566992e1]521 futex_down(&async_futex);
[a35b458]522
[566992e1]523 ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
524 if (link)
525 interface = hash_table_get_inst(link, interface_t, link);
526 else
527 interface = async_new_interface(iface);
[a35b458]528
[566992e1]529 if (!interface) {
530 futex_up(&async_futex);
531 return ENOMEM;
532 }
[a35b458]533
[566992e1]534 port_t *port = async_new_port(interface, handler, data);
535 if (!port) {
536 futex_up(&async_futex);
537 return ENOMEM;
538 }
[a35b458]539
[566992e1]540 *port_id = port->id;
[a35b458]541
[566992e1]542 futex_up(&async_futex);
[a35b458]543
[566992e1]544 return EOK;
545}
546
[b688fd8]547void async_set_fallback_port_handler(async_port_handler_t handler, void *data)
548{
549 assert(handler != NULL);
[a35b458]550
[b688fd8]551 fallback_port_handler = handler;
552 fallback_port_data = data;
553}
554
[c80fdd0]555static hash_table_t client_hash_table;
[c07544d3]556static hash_table_t conn_hash_table;
[8820544]557static hash_table_t notification_hash_table;
[c07544d3]558static LIST_INITIALIZE(timeout_list);
559
[8820544]560static sysarg_t notification_avail = 0;
561
562static size_t client_key_hash(void *key)
[c80fdd0]563{
[8820544]564 task_id_t in_task_id = *(task_id_t *) key;
565 return in_task_id;
[c80fdd0]566}
567
[062d900]568static size_t client_hash(const ht_link_t *item)
[c80fdd0]569{
[062d900]570 client_t *client = hash_table_get_inst(item, client_t, link);
571 return client_key_hash(&client->in_task_id);
[c80fdd0]572}
573
[8820544]574static bool client_key_equal(void *key, const ht_link_t *item)
[c80fdd0]575{
[8820544]576 task_id_t in_task_id = *(task_id_t *) key;
[062d900]577 client_t *client = hash_table_get_inst(item, client_t, link);
[8820544]578 return in_task_id == client->in_task_id;
[c80fdd0]579}
580
581/** Operations for the client hash table. */
[062d900]582static hash_table_ops_t client_hash_table_ops = {
[c80fdd0]583 .hash = client_hash,
[062d900]584 .key_hash = client_key_hash,
585 .key_equal = client_key_equal,
[4e00f87]586 .equal = NULL,
587 .remove_callback = NULL
[c80fdd0]588};
[80649a91]589
[853802e]590typedef struct {
591 task_id_t task_id;
592 sysarg_t phone_hash;
593} conn_key_t;
594
595/** Compute hash into the connection hash table
[e70bfa5]596 *
[853802e]597 * The hash is based on the source task ID and the source phone hash. The task
598 * ID is included in the hash because a phone hash alone might not be unique
599 * while we still track connections for killed tasks due to kernel's recycling
600 * of phone structures.
601 *
602 * @param key Pointer to the connection key structure.
[c07544d3]603 *
604 * @return Index into the connection hash table.
[e70bfa5]605 *
606 */
[062d900]607static size_t conn_key_hash(void *key)
[450cd3a]608{
[853802e]609 conn_key_t *ck = (conn_key_t *) key;
610
611 size_t hash = 0;
612 hash = hash_combine(hash, LOWER32(ck->task_id));
613 hash = hash_combine(hash, UPPER32(ck->task_id));
614 hash = hash_combine(hash, ck->phone_hash);
615 return hash;
[450cd3a]616}
[06502f7d]617
[062d900]618static size_t conn_hash(const ht_link_t *item)
[450cd3a]619{
[062d900]620 connection_t *conn = hash_table_get_inst(item, connection_t, link);
[853802e]621 return conn_key_hash(&(conn_key_t){
622 .task_id = conn->in_task_id,
623 .phone_hash = conn->in_phone_hash
624 });
[450cd3a]625}
[06502f7d]626
[062d900]627static bool conn_key_equal(void *key, const ht_link_t *item)
[450cd3a]628{
[853802e]629 conn_key_t *ck = (conn_key_t *) key;
[062d900]630 connection_t *conn = hash_table_get_inst(item, connection_t, link);
[853802e]631 return ((ck->task_id == conn->in_task_id) &&
632 (ck->phone_hash == conn->in_phone_hash));
[450cd3a]633}
634
[e70bfa5]635/** Operations for the connection hash table. */
[062d900]636static hash_table_ops_t conn_hash_table_ops = {
[80649a91]637 .hash = conn_hash,
[062d900]638 .key_hash = conn_key_hash,
639 .key_equal = conn_key_equal,
[4e00f87]640 .equal = NULL,
641 .remove_callback = NULL
[80649a91]642};
643
[9ef495f]644static client_t *async_client_get(task_id_t client_id, bool create)
645{
646 client_t *client = NULL;
[a35b458]647
[9ef495f]648 futex_down(&async_futex);
649 ht_link_t *link = hash_table_find(&client_hash_table, &client_id);
650 if (link) {
651 client = hash_table_get_inst(link, client_t, link);
652 atomic_inc(&client->refcnt);
653 } else if (create) {
654 client = malloc(sizeof(client_t));
655 if (client) {
656 client->in_task_id = client_id;
657 client->data = async_client_data_create();
[a35b458]658
[9ef495f]659 atomic_set(&client->refcnt, 1);
660 hash_table_insert(&client_hash_table, &client->link);
661 }
662 }
[a35b458]663
[9ef495f]664 futex_up(&async_futex);
665 return client;
666}
667
668static void async_client_put(client_t *client)
669{
670 bool destroy;
[a35b458]671
[9ef495f]672 futex_down(&async_futex);
[a35b458]673
[9ef495f]674 if (atomic_predec(&client->refcnt) == 0) {
675 hash_table_remove(&client_hash_table, &client->in_task_id);
676 destroy = true;
677 } else
678 destroy = false;
[a35b458]679
[9ef495f]680 futex_up(&async_futex);
[a35b458]681
[9ef495f]682 if (destroy) {
683 if (client->data)
684 async_client_data_destroy(client->data);
[a35b458]685
[9ef495f]686 free(client);
687 }
688}
689
690/** Wrapper for client connection fibril.
691 *
692 * When a new connection arrives, a fibril with this implementing
693 * function is created.
694 *
695 * @param arg Connection structure pointer.
696 *
697 * @return Always zero.
698 *
699 */
[b7fd2a0]700static errno_t connection_fibril(void *arg)
[9ef495f]701{
702 assert(arg);
[a35b458]703
[9ef495f]704 /*
705 * Setup fibril-local connection pointer.
706 */
707 fibril_connection = (connection_t *) arg;
[a35b458]708
[9ef495f]709 /*
710 * Add our reference for the current connection in the client task
711 * tracking structure. If this is the first reference, create and
712 * hash in a new tracking structure.
713 */
[a35b458]714
[9ef495f]715 client_t *client = async_client_get(fibril_connection->in_task_id, true);
716 if (!client) {
[01c3bb4]717 ipc_answer_0(fibril_connection->chandle, ENOMEM);
[9ef495f]718 return 0;
719 }
[a35b458]720
[9ef495f]721 fibril_connection->client = client;
[a35b458]722
[9ef495f]723 /*
724 * Call the connection handler function.
725 */
[01c3bb4]726 fibril_connection->handler(fibril_connection->chandle,
[9ef495f]727 &fibril_connection->call, fibril_connection->data);
[a35b458]728
[9ef495f]729 /*
730 * Remove the reference for this client task connection.
731 */
732 async_client_put(client);
[a35b458]733
[9ef495f]734 /*
735 * Remove myself from the connection hash table.
736 */
737 futex_down(&async_futex);
[853802e]738 hash_table_remove(&conn_hash_table, &(conn_key_t){
739 .task_id = fibril_connection->in_task_id,
740 .phone_hash = fibril_connection->in_phone_hash
741 });
[9ef495f]742 futex_up(&async_futex);
[a35b458]743
[9ef495f]744 /*
745 * Answer all remaining messages with EHANGUP.
746 */
747 while (!list_empty(&fibril_connection->msg_queue)) {
748 msg_t *msg =
749 list_get_instance(list_first(&fibril_connection->msg_queue),
750 msg_t, link);
[a35b458]751
[9ef495f]752 list_remove(&msg->link);
[01c3bb4]753 ipc_answer_0(msg->chandle, EHANGUP);
[9ef495f]754 free(msg);
755 }
[a35b458]756
[9ef495f]757 /*
758 * If the connection was hung-up, answer the last call,
759 * i.e. IPC_M_PHONE_HUNGUP.
760 */
[01c3bb4]761 if (fibril_connection->close_chandle)
762 ipc_answer_0(fibril_connection->close_chandle, EOK);
[a35b458]763
[9ef495f]764 free(fibril_connection);
[d5c1051]765 return EOK;
[9ef495f]766}
767
768/** Create a new fibril for a new connection.
769 *
[01c3bb4]770 * Create new fibril for connection, fill in connection structures and insert it
771 * into the hash table, so that later we can easily do routing of messages to
772 * particular fibrils.
[9ef495f]773 *
[01c3bb4]774 * @param in_task_id Identification of the incoming connection.
775 * @param in_phone_hash Identification of the incoming connection.
776 * @param chandle Handle of the opening IPC_M_CONNECT_ME_TO call.
777 * If chandle is CAP_NIL, the connection was opened by
778 * accepting the IPC_M_CONNECT_TO_ME call and this
779 * function is called directly by the server.
780 * @param call Call data of the opening call.
781 * @param handler Connection handler.
782 * @param data Client argument to pass to the connection handler.
[9ef495f]783 *
[01c3bb4]784 * @return New fibril id or NULL on failure.
[9ef495f]785 *
786 */
787static fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
[01c3bb4]788 cap_handle_t chandle, ipc_call_t *call, async_port_handler_t handler,
[9ef495f]789 void *data)
790{
791 connection_t *conn = malloc(sizeof(*conn));
792 if (!conn) {
[01c3bb4]793 if (chandle != CAP_NIL)
794 ipc_answer_0(chandle, ENOMEM);
[a35b458]795
[9ef495f]796 return (uintptr_t) NULL;
797 }
[a35b458]798
[9ef495f]799 conn->in_task_id = in_task_id;
800 conn->in_phone_hash = in_phone_hash;
801 list_initialize(&conn->msg_queue);
[01c3bb4]802 conn->chandle = chandle;
803 conn->close_chandle = CAP_NIL;
[9ef495f]804 conn->handler = handler;
805 conn->data = data;
[a35b458]806
[9ef495f]807 if (call)
808 conn->call = *call;
[a35b458]809
[9ef495f]810 /* We will activate the fibril ASAP */
811 conn->wdata.active = true;
812 conn->wdata.fid = fibril_create(connection_fibril, conn);
[a35b458]813
[9ef495f]814 if (conn->wdata.fid == 0) {
815 free(conn);
[a35b458]816
[01c3bb4]817 if (chandle != CAP_NIL)
818 ipc_answer_0(chandle, ENOMEM);
[a35b458]819
[9ef495f]820 return (uintptr_t) NULL;
821 }
[a35b458]822
[9ef495f]823 /* Add connection to the connection hash table */
[a35b458]824
[9ef495f]825 futex_down(&async_futex);
826 hash_table_insert(&conn_hash_table, &conn->link);
827 futex_up(&async_futex);
[a35b458]828
[9ef495f]829 fibril_add_ready(conn->wdata.fid);
[a35b458]830
[9ef495f]831 return conn->wdata.fid;
832}
833
[78bb04b]834/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
835 *
836 * Ask through phone for a new connection to some service.
837 *
838 * @param exch Exchange for sending the message.
839 * @param iface Callback interface.
840 * @param arg1 User defined argument.
841 * @param arg2 User defined argument.
842 * @param handler Callback handler.
843 * @param data Handler data.
844 * @param port_id ID of the newly created port.
845 *
[cde999a]846 * @return Zero on success or an error code.
[78bb04b]847 *
848 */
[b7fd2a0]849errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
[78bb04b]850 sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
851{
852 if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
853 return EINVAL;
[a35b458]854
[78bb04b]855 if (exch == NULL)
856 return ENOENT;
[a35b458]857
[78bb04b]858 ipc_call_t answer;
859 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
860 &answer);
[a35b458]861
[b7fd2a0]862 errno_t ret;
[78bb04b]863 async_wait_for(req, &ret);
864 if (ret != EOK)
[b7fd2a0]865 return (errno_t) ret;
[a35b458]866
[78bb04b]867 sysarg_t phone_hash = IPC_GET_ARG5(answer);
868 interface_t *interface;
[a35b458]869
[78bb04b]870 futex_down(&async_futex);
[a35b458]871
[78bb04b]872 ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
873 if (link)
874 interface = hash_table_get_inst(link, interface_t, link);
875 else
876 interface = async_new_interface(iface);
[a35b458]877
[78bb04b]878 if (!interface) {
879 futex_up(&async_futex);
880 return ENOMEM;
881 }
[a35b458]882
[78bb04b]883 port_t *port = async_new_port(interface, handler, data);
884 if (!port) {
885 futex_up(&async_futex);
886 return ENOMEM;
887 }
[a35b458]888
[78bb04b]889 *port_id = port->id;
[a35b458]890
[78bb04b]891 futex_up(&async_futex);
[a35b458]892
[78bb04b]893 fid_t fid = async_new_connection(answer.in_task_id, phone_hash,
[01c3bb4]894 CAP_NIL, NULL, handler, data);
[78bb04b]895 if (fid == (uintptr_t) NULL)
896 return ENOMEM;
[a35b458]897
[78bb04b]898 return EOK;
899}
900
[8820544]901static size_t notification_key_hash(void *key)
902{
903 sysarg_t id = *(sysarg_t *) key;
904 return id;
905}
906
907static size_t notification_hash(const ht_link_t *item)
908{
909 notification_t *notification =
910 hash_table_get_inst(item, notification_t, link);
911 return notification_key_hash(&notification->imethod);
912}
913
914static bool notification_key_equal(void *key, const ht_link_t *item)
915{
916 sysarg_t id = *(sysarg_t *) key;
917 notification_t *notification =
918 hash_table_get_inst(item, notification_t, link);
919 return id == notification->imethod;
920}
921
922/** Operations for the notification hash table. */
923static hash_table_ops_t notification_hash_table_ops = {
924 .hash = notification_hash,
925 .key_hash = notification_key_hash,
926 .key_equal = notification_key_equal,
927 .equal = NULL,
928 .remove_callback = NULL
929};
930
[e70bfa5]931/** Sort in current fibril's timeout request.
[49d072e]932 *
[c07544d3]933 * @param wd Wait data of the current fibril.
934 *
[49d072e]935 */
[b6ee5b1]936void async_insert_timeout(awaiter_t *wd)
[49d072e]937{
[79ae36dd]938 assert(wd);
[a35b458]939
[f53cc81]940 wd->to_event.occurred = false;
941 wd->to_event.inlist = true;
[a35b458]942
[b72efe8]943 link_t *tmp = timeout_list.head.next;
944 while (tmp != &timeout_list.head) {
[47b7006]945 awaiter_t *cur
946 = list_get_instance(tmp, awaiter_t, to_event.link);
[a35b458]947
[f53cc81]948 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
[49d072e]949 break;
[a35b458]950
[49d072e]951 tmp = tmp->next;
952 }
[a35b458]953
[b72efe8]954 list_insert_before(&wd->to_event.link, tmp);
[49d072e]955}
956
[e70bfa5]957/** Try to route a call to an appropriate connection fibril.
[80649a91]958 *
[36c9234]959 * If the proper connection fibril is found, a message with the call is added to
960 * its message queue. If the fibril was not active, it is activated and all
961 * timeouts are unregistered.
962 *
[01c3bb4]963 * @param chandle Handle of the incoming call.
964 * @param call Data of the incoming call.
[c07544d3]965 *
966 * @return False if the call doesn't match any connection.
[47b7006]967 * @return True if the call was passed to the respective connection fibril.
[36c9234]968 *
[80649a91]969 */
[01c3bb4]970static bool route_call(cap_handle_t chandle, ipc_call_t *call)
[450cd3a]971{
[79ae36dd]972 assert(call);
[a35b458]973
[01ff41c]974 futex_down(&async_futex);
[a35b458]975
[853802e]976 ht_link_t *link = hash_table_find(&conn_hash_table, &(conn_key_t){
977 .task_id = call->in_task_id,
978 .phone_hash = call->in_phone_hash
979 });
[8820544]980 if (!link) {
[01ff41c]981 futex_up(&async_futex);
[c07544d3]982 return false;
[450cd3a]983 }
[a35b458]984
[8820544]985 connection_t *conn = hash_table_get_inst(link, connection_t, link);
[a35b458]986
[c07544d3]987 msg_t *msg = malloc(sizeof(*msg));
988 if (!msg) {
989 futex_up(&async_futex);
990 return false;
991 }
[a35b458]992
[01c3bb4]993 msg->chandle = chandle;
[80649a91]994 msg->call = *call;
995 list_append(&msg->link, &conn->msg_queue);
[a35b458]996
[228e490]997 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
[01c3bb4]998 conn->close_chandle = chandle;
[a35b458]999
[36c9234]1000 /* If the connection fibril is waiting for an event, activate it */
[49d072e]1001 if (!conn->wdata.active) {
[a35b458]1002
[49d072e]1003 /* If in timeout list, remove it */
[f53cc81]1004 if (conn->wdata.to_event.inlist) {
1005 conn->wdata.to_event.inlist = false;
1006 list_remove(&conn->wdata.to_event.link);
[49d072e]1007 }
[a35b458]1008
[c07544d3]1009 conn->wdata.active = true;
[bc1f1c2]1010 fibril_add_ready(conn->wdata.fid);
[80649a91]1011 }
[a35b458]1012
[01ff41c]1013 futex_up(&async_futex);
[c07544d3]1014 return true;
1015}
[80649a91]1016
[c170438]1017/** Process notification.
[c07544d3]1018 *
[c170438]1019 * @param call Data of the incoming call.
[58563585]1020 *
[c07544d3]1021 */
[01c3bb4]1022static void process_notification(ipc_call_t *call)
[c07544d3]1023{
[8820544]1024 async_notification_handler_t handler = NULL;
1025 void *data = NULL;
[c170438]1026
1027 assert(call);
[a35b458]1028
[8820544]1029 futex_down(&async_futex);
[a35b458]1030
[8820544]1031 ht_link_t *link = hash_table_find(&notification_hash_table,
[c170438]1032 &IPC_GET_IMETHOD(*call));
[8820544]1033 if (link) {
1034 notification_t *notification =
1035 hash_table_get_inst(link, notification_t, link);
1036 handler = notification->handler;
1037 data = notification->data;
1038 }
[a35b458]1039
[8820544]1040 futex_up(&async_futex);
[a35b458]1041
[8820544]1042 if (handler)
[01c3bb4]1043 handler(call, data);
[80649a91]1044}
1045
[8820544]1046/** Subscribe to IRQ notification.
1047 *
1048 * @param inr IRQ number.
1049 * @param handler Notification handler.
1050 * @param data Notification handler client data.
1051 * @param ucode Top-half pseudocode handler.
1052 *
[071a1ddb]1053 * @param[out] handle IRQ capability handle on success.
1054 *
[cde999a]1055 * @return An error code.
[8820544]1056 *
1057 */
[b7fd2a0]1058errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
[071a1ddb]1059 void *data, const irq_code_t *ucode, cap_handle_t *handle)
[8820544]1060{
1061 notification_t *notification =
1062 (notification_t *) malloc(sizeof(notification_t));
1063 if (!notification)
1064 return ENOMEM;
[a35b458]1065
[8820544]1066 futex_down(&async_futex);
[a35b458]1067
[8820544]1068 sysarg_t imethod = notification_avail;
1069 notification_avail++;
[a35b458]1070
[8820544]1071 notification->imethod = imethod;
1072 notification->handler = handler;
1073 notification->data = data;
[a35b458]1074
[8820544]1075 hash_table_insert(&notification_hash_table, &notification->link);
[a35b458]1076
[8820544]1077 futex_up(&async_futex);
[a35b458]1078
[9233e9d]1079 cap_handle_t cap;
[b7fd2a0]1080 errno_t rc = ipc_irq_subscribe(inr, imethod, ucode, &cap);
[071a1ddb]1081 if (rc == EOK && handle != NULL) {
1082 *handle = cap;
[9233e9d]1083 }
[071a1ddb]1084 return rc;
[8820544]1085}
1086
1087/** Unsubscribe from IRQ notification.
1088 *
[1b20da0]1089 * @param cap IRQ capability handle.
[8820544]1090 *
[cde999a]1091 * @return Zero on success or an error code.
[8820544]1092 *
1093 */
[b7fd2a0]1094errno_t async_irq_unsubscribe(int cap)
[8820544]1095{
1096 // TODO: Remove entry from hash table
1097 // to avoid memory leak
[a35b458]1098
[e9d15d9]1099 return ipc_irq_unsubscribe(cap);
[8820544]1100}
1101
1102/** Subscribe to event notifications.
1103 *
1104 * @param evno Event type to subscribe.
1105 * @param handler Notification handler.
1106 * @param data Notification handler client data.
1107 *
[cde999a]1108 * @return Zero on success or an error code.
[8820544]1109 *
1110 */
[b7fd2a0]1111errno_t async_event_subscribe(event_type_t evno,
[8820544]1112 async_notification_handler_t handler, void *data)
1113{
1114 notification_t *notification =
1115 (notification_t *) malloc(sizeof(notification_t));
1116 if (!notification)
1117 return ENOMEM;
[a35b458]1118
[8820544]1119 futex_down(&async_futex);
[a35b458]1120
[8820544]1121 sysarg_t imethod = notification_avail;
1122 notification_avail++;
[a35b458]1123
[8820544]1124 notification->imethod = imethod;
1125 notification->handler = handler;
1126 notification->data = data;
[a35b458]1127
[8820544]1128 hash_table_insert(&notification_hash_table, &notification->link);
[a35b458]1129
[8820544]1130 futex_up(&async_futex);
[a35b458]1131
[8820544]1132 return ipc_event_subscribe(evno, imethod);
1133}
1134
1135/** Subscribe to task event notifications.
1136 *
1137 * @param evno Event type to subscribe.
1138 * @param handler Notification handler.
1139 * @param data Notification handler client data.
1140 *
[cde999a]1141 * @return Zero on success or an error code.
[8820544]1142 *
1143 */
[b7fd2a0]1144errno_t async_event_task_subscribe(event_task_type_t evno,
[8820544]1145 async_notification_handler_t handler, void *data)
1146{
1147 notification_t *notification =
1148 (notification_t *) malloc(sizeof(notification_t));
1149 if (!notification)
1150 return ENOMEM;
[a35b458]1151
[8820544]1152 futex_down(&async_futex);
[a35b458]1153
[8820544]1154 sysarg_t imethod = notification_avail;
1155 notification_avail++;
[a35b458]1156
[8820544]1157 notification->imethod = imethod;
1158 notification->handler = handler;
1159 notification->data = data;
[a35b458]1160
[8820544]1161 hash_table_insert(&notification_hash_table, &notification->link);
[a35b458]1162
[8820544]1163 futex_up(&async_futex);
[a35b458]1164
[8820544]1165 return ipc_event_task_subscribe(evno, imethod);
1166}
1167
1168/** Unmask event notifications.
1169 *
1170 * @param evno Event type to unmask.
1171 *
1172 * @return Value returned by the kernel.
1173 *
1174 */
[b7fd2a0]1175errno_t async_event_unmask(event_type_t evno)
[8820544]1176{
1177 return ipc_event_unmask(evno);
1178}
1179
1180/** Unmask task event notifications.
1181 *
1182 * @param evno Event type to unmask.
1183 *
1184 * @return Value returned by the kernel.
1185 *
1186 */
[b7fd2a0]1187errno_t async_event_task_unmask(event_task_type_t evno)
[8820544]1188{
1189 return ipc_event_task_unmask(evno);
1190}
1191
[e70bfa5]1192/** Return new incoming message for the current (fibril-local) connection.
1193 *
[01c3bb4]1194 * @param call Storage where the incoming call data will be stored.
1195 * @param usecs Timeout in microseconds. Zero denotes no timeout.
[e70bfa5]1196 *
[01c3bb4]1197 * @return If no timeout was specified, then a handle of the incoming call is
1198 * returned. If a timeout is specified, then a handle of the incoming
1199 * call is returned unless the timeout expires prior to receiving a
1200 * message. In that case zero CAP_NIL is returned.
[e70bfa5]1201 */
[01c3bb4]1202cap_handle_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
[80649a91]1203{
[79ae36dd]1204 assert(call);
1205 assert(fibril_connection);
[a35b458]1206
[c07544d3]1207 /* Why doing this?
[79ae36dd]1208 * GCC 4.1.0 coughs on fibril_connection-> dereference.
[6c46350]1209 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
[c07544d3]1210 * I would never expect to find so many errors in
1211 * a compiler.
[6c46350]1212 */
[79ae36dd]1213 connection_t *conn = fibril_connection;
[a35b458]1214
[01ff41c]1215 futex_down(&async_futex);
[a35b458]1216
[49d072e]1217 if (usecs) {
[45cbcaf4]1218 getuptime(&conn->wdata.to_event.expires);
[7f9d97f3]1219 tv_add_diff(&conn->wdata.to_event.expires, usecs);
[c07544d3]1220 } else
[f53cc81]1221 conn->wdata.to_event.inlist = false;
[a35b458]1222
[e70bfa5]1223 /* If nothing in queue, wait until something arrives */
[6c46350]1224 while (list_empty(&conn->msg_queue)) {
[01c3bb4]1225 if (conn->close_chandle) {
[8c8f8d6]1226 /*
1227 * Handle the case when the connection was already
1228 * closed by the client but the server did not notice
1229 * the first IPC_M_PHONE_HUNGUP call and continues to
1230 * call async_get_call_timeout(). Repeat
[47b7006]1231 * IPC_M_PHONE_HUNGUP until the caller notices.
[8c8f8d6]1232 */
1233 memset(call, 0, sizeof(ipc_call_t));
[228e490]1234 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
[8c8f8d6]1235 futex_up(&async_futex);
[01c3bb4]1236 return conn->close_chandle;
[8c8f8d6]1237 }
[a35b458]1238
[085bd54]1239 if (usecs)
[b6ee5b1]1240 async_insert_timeout(&conn->wdata);
[a35b458]1241
[c07544d3]1242 conn->wdata.active = false;
[a35b458]1243
[c7509e5]1244 /*
1245 * Note: the current fibril will be rescheduled either due to a
1246 * timeout or due to an arriving message destined to it. In the
1247 * former case, handle_expired_timeouts() and, in the latter
1248 * case, route_call() will perform the wakeup.
1249 */
[116d3f6f]1250 fibril_switch(FIBRIL_TO_MANAGER);
[a35b458]1251
[e70bfa5]1252 /*
[c07544d3]1253 * Futex is up after getting back from async_manager.
1254 * Get it again.
[c7509e5]1255 */
[49d072e]1256 futex_down(&async_futex);
[f53cc81]1257 if ((usecs) && (conn->wdata.to_event.occurred)
[c07544d3]1258 && (list_empty(&conn->msg_queue))) {
[e70bfa5]1259 /* If we timed out -> exit */
[49d072e]1260 futex_up(&async_futex);
[01c3bb4]1261 return CAP_NIL;
[49d072e]1262 }
[450cd3a]1263 }
[a35b458]1264
[57dea62]1265 msg_t *msg = list_get_instance(list_first(&conn->msg_queue),
1266 msg_t, link);
[80649a91]1267 list_remove(&msg->link);
[a35b458]1268
[01c3bb4]1269 cap_handle_t chandle = msg->chandle;
[80649a91]1270 *call = msg->call;
1271 free(msg);
[a35b458]1272
[01ff41c]1273 futex_up(&async_futex);
[01c3bb4]1274 return chandle;
[80649a91]1275}
1276
[455f190]1277void *async_get_client_data(void)
1278{
1279 assert(fibril_connection);
1280 return fibril_connection->client->data;
1281}
1282
[e2ab36f1]1283void *async_get_client_data_by_id(task_id_t client_id)
[455f190]1284{
[e2ab36f1]1285 client_t *client = async_client_get(client_id, false);
[455f190]1286 if (!client)
1287 return NULL;
[a35b458]1288
[455f190]1289 if (!client->data) {
1290 async_client_put(client);
1291 return NULL;
1292 }
[a35b458]1293
[455f190]1294 return client->data;
1295}
1296
[e2ab36f1]1297void async_put_client_data_by_id(task_id_t client_id)
[455f190]1298{
[e2ab36f1]1299 client_t *client = async_client_get(client_id, false);
[a35b458]1300
[455f190]1301 assert(client);
1302 assert(client->data);
[a35b458]1303
[cdc8ee2d]1304 /* Drop the reference we got in async_get_client_data_by_hash(). */
1305 async_client_put(client);
[a35b458]1306
[cdc8ee2d]1307 /* Drop our own reference we got at the beginning of this function. */
[455f190]1308 async_client_put(client);
1309}
1310
[566992e1]1311static port_t *async_find_port(iface_t iface, port_id_t port_id)
1312{
1313 port_t *port = NULL;
[a35b458]1314
[566992e1]1315 futex_down(&async_futex);
[a35b458]1316
[566992e1]1317 ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
1318 if (link) {
1319 interface_t *interface =
1320 hash_table_get_inst(link, interface_t, link);
[a35b458]1321
[566992e1]1322 link = hash_table_find(&interface->port_hash_table, &port_id);
1323 if (link)
1324 port = hash_table_get_inst(link, port_t, link);
1325 }
[a35b458]1326
[566992e1]1327 futex_up(&async_futex);
[a35b458]1328
[566992e1]1329 return port;
1330}
1331
[36c9234]1332/** Handle a call that was received.
1333 *
1334 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
1335 * Otherwise the call is routed to its connection fibril.
1336 *
[01c3bb4]1337 * @param chandle Handle of the incoming call.
1338 * @param call Data of the incoming call.
[6b21292]1339 *
[36c9234]1340 */
[01c3bb4]1341static void handle_call(cap_handle_t chandle, ipc_call_t *call)
[80649a91]1342{
[79ae36dd]1343 assert(call);
[a35b458]1344
[b688fd8]1345 /* Kernel notification */
[addbce4]1346 if ((chandle == CAP_NIL) && (call->flags & IPC_CALL_NOTIF)) {
[c170438]1347 fibril_t *fibril = (fibril_t *) __tcb_get()->fibril_data;
1348 unsigned oldsw = fibril->switches;
[a35b458]1349
[01c3bb4]1350 process_notification(call);
[a35b458]1351
[c170438]1352 if (oldsw != fibril->switches) {
1353 /*
1354 * The notification handler did not execute atomically
1355 * and so the current manager fibril assumed the role of
1356 * a notification fibril. While waiting for its
1357 * resources, it switched to another manager fibril that
1358 * had already existed or it created a new one. We
1359 * therefore know there is at least yet another
1360 * manager fibril that can take over. We now kill the
1361 * current 'notification' fibril to prevent fibril
1362 * population explosion.
1363 */
1364 futex_down(&async_futex);
1365 fibril_switch(FIBRIL_FROM_DEAD);
1366 }
[a35b458]1367
[47b7006]1368 return;
[6b21292]1369 }
[a35b458]1370
[566992e1]1371 /* New connection */
1372 if (IPC_GET_IMETHOD(*call) == IPC_M_CONNECT_ME_TO) {
1373 iface_t iface = (iface_t) IPC_GET_ARG1(*call);
1374 sysarg_t in_phone_hash = IPC_GET_ARG5(*call);
[a35b458]1375
[01c3bb4]1376 async_port_handler_t handler = fallback_port_handler;
[566992e1]1377 void *data = fallback_port_data;
[a35b458]1378
[566992e1]1379 // TODO: Currently ignores all ports but the first one
1380 port_t *port = async_find_port(iface, 0);
1381 if (port) {
1382 handler = port->handler;
1383 data = port->data;
1384 }
[a35b458]1385
[01c3bb4]1386 async_new_connection(call->in_task_id, in_phone_hash, chandle,
[566992e1]1387 call, handler, data);
1388 return;
1389 }
[a35b458]1390
[36c9234]1391 /* Try to route the call through the connection hash table */
[01c3bb4]1392 if (route_call(chandle, call))
[47b7006]1393 return;
[a35b458]1394
[44c6d88d]1395 /* Unknown call from unknown phone - hang it up */
[01c3bb4]1396 ipc_answer_0(chandle, EHANGUP);
[450cd3a]1397}
1398
[f2f0392]1399/** Fire all timeouts that expired. */
[c042bdd]1400static void handle_expired_timeouts(void)
1401{
1402 struct timeval tv;
[45cbcaf4]1403 getuptime(&tv);
[a35b458]1404
[c042bdd]1405 futex_down(&async_futex);
[a35b458]1406
[b72efe8]1407 link_t *cur = list_first(&timeout_list);
1408 while (cur != NULL) {
[47b7006]1409 awaiter_t *waiter =
1410 list_get_instance(cur, awaiter_t, to_event.link);
[a35b458]1411
[f53cc81]1412 if (tv_gt(&waiter->to_event.expires, &tv))
[c042bdd]1413 break;
[a35b458]1414
[f53cc81]1415 list_remove(&waiter->to_event.link);
1416 waiter->to_event.inlist = false;
1417 waiter->to_event.occurred = true;
[a35b458]1418
[36c9234]1419 /*
[c07544d3]1420 * Redundant condition?
1421 * The fibril should not be active when it gets here.
[c042bdd]1422 */
[49d072e]1423 if (!waiter->active) {
[c07544d3]1424 waiter->active = true;
[bc1f1c2]1425 fibril_add_ready(waiter->fid);
[c042bdd]1426 }
[a35b458]1427
[b72efe8]1428 cur = list_first(&timeout_list);
[c042bdd]1429 }
[a35b458]1430
[c042bdd]1431 futex_up(&async_futex);
1432}
1433
[36c9234]1434/** Endless loop dispatching incoming calls and answers.
1435 *
[c07544d3]1436 * @return Never returns.
1437 *
[36c9234]1438 */
[b7fd2a0]1439static errno_t async_manager_worker(void)
[80649a91]1440{
[c07544d3]1441 while (true) {
[116d3f6f]1442 if (fibril_switch(FIBRIL_FROM_MANAGER)) {
[47b7006]1443 futex_up(&async_futex);
[36c9234]1444 /*
1445 * async_futex is always held when entering a manager
1446 * fibril.
[a46da63]1447 */
[80649a91]1448 continue;
1449 }
[a35b458]1450
[c042bdd]1451 futex_down(&async_futex);
[a35b458]1452
[c07544d3]1453 suseconds_t timeout;
[1db6dfd]1454 unsigned int flags = SYNCH_FLAGS_NONE;
[c042bdd]1455 if (!list_empty(&timeout_list)) {
[b72efe8]1456 awaiter_t *waiter = list_get_instance(
1457 list_first(&timeout_list), awaiter_t, to_event.link);
[a35b458]1458
[c07544d3]1459 struct timeval tv;
[45cbcaf4]1460 getuptime(&tv);
[a35b458]1461
[f53cc81]1462 if (tv_gteq(&tv, &waiter->to_event.expires)) {
[6c46350]1463 futex_up(&async_futex);
[c042bdd]1464 handle_expired_timeouts();
[1db6dfd]1465 /*
1466 * Notice that even if the event(s) already
1467 * expired (and thus the other fibril was
1468 * supposed to be running already),
1469 * we check for incoming IPC.
1470 *
1471 * Otherwise, a fibril that continuously
1472 * creates (almost) expired events could
1473 * prevent IPC retrieval from the kernel.
1474 */
1475 timeout = 0;
1476 flags = SYNCH_FLAGS_NON_BLOCKING;
1477
1478 } else {
[7f9d97f3]1479 timeout = tv_sub_diff(&waiter->to_event.expires,
1480 &tv);
[1db6dfd]1481 futex_up(&async_futex);
1482 }
1483 } else {
1484 futex_up(&async_futex);
[0b99e40]1485 timeout = SYNCH_NO_TIMEOUT;
[1db6dfd]1486 }
[a35b458]1487
[8619f25]1488 atomic_inc(&threads_in_ipc_wait);
[a35b458]1489
[c07544d3]1490 ipc_call_t call;
[b7fd2a0]1491 errno_t rc = ipc_wait_cycle(&call, timeout, flags);
[a35b458]1492
[8619f25]1493 atomic_dec(&threads_in_ipc_wait);
[a35b458]1494
[6deb2cd]1495 assert(rc == EOK);
[01c3bb4]1496
[6deb2cd]1497 if (call.cap_handle == CAP_NIL) {
[a1026da]1498 if ((call.flags &
1499 (IPC_CALL_NOTIF | IPC_CALL_ANSWERED)) == 0) {
1500 /* Neither a notification nor an answer. */
[01c3bb4]1501 handle_expired_timeouts();
1502 continue;
1503 }
[0b99e40]1504 }
[01c3bb4]1505
[addbce4]1506 if (call.flags & IPC_CALL_ANSWERED)
[80649a91]1507 continue;
[01c3bb4]1508
[6deb2cd]1509 handle_call(call.cap_handle, &call);
[80649a91]1510 }
[01c3bb4]1511
[a46da63]1512 return 0;
[80649a91]1513}
1514
[36c9234]1515/** Function to start async_manager as a standalone fibril.
[c07544d3]1516 *
[36c9234]1517 * When more kernel threads are used, one async manager should exist per thread.
1518 *
[c07544d3]1519 * @param arg Unused.
1520 * @return Never returns.
[36c9234]1521 *
[a2cd194]1522 */
[b7fd2a0]1523static errno_t async_manager_fibril(void *arg)
[80649a91]1524{
[a46da63]1525 futex_up(&async_futex);
[a35b458]1526
[36c9234]1527 /*
1528 * async_futex is always locked when entering manager
1529 */
[085bd54]1530 async_manager_worker();
[a35b458]1531
[a46da63]1532 return 0;
[80649a91]1533}
[450cd3a]1534
[36c9234]1535/** Add one manager to manager list. */
[80649a91]1536void async_create_manager(void)
[450cd3a]1537{
[c170438]1538 fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
[86d7bfa]1539 if (fid != 0)
1540 fibril_add_manager(fid);
[80649a91]1541}
1542
1543/** Remove one manager from manager list */
1544void async_destroy_manager(void)
1545{
[bc1f1c2]1546 fibril_remove_manager();
[80649a91]1547}
1548
[36c9234]1549/** Initialize the async framework.
1550 *
1551 */
[47b7006]1552void __async_init(void)
[80649a91]1553{
[566992e1]1554 if (!hash_table_create(&interface_hash_table, 0, 0,
1555 &interface_hash_table_ops))
1556 abort();
[a35b458]1557
[062d900]1558 if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
[47b7006]1559 abort();
[a35b458]1560
[062d900]1561 if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
[47b7006]1562 abort();
[a35b458]1563
[8820544]1564 if (!hash_table_create(&notification_hash_table, 0, 0,
1565 &notification_hash_table_ops))
1566 abort();
[a35b458]1567
[79ae36dd]1568 session_ns = (async_sess_t *) malloc(sizeof(async_sess_t));
1569 if (session_ns == NULL)
1570 abort();
[a35b458]1571
[566992e1]1572 session_ns->iface = 0;
[79ae36dd]1573 session_ns->mgmt = EXCHANGE_ATOMIC;
1574 session_ns->phone = PHONE_NS;
1575 session_ns->arg1 = 0;
1576 session_ns->arg2 = 0;
1577 session_ns->arg3 = 0;
[a35b458]1578
[58cbf8d5]1579 fibril_mutex_initialize(&session_ns->remote_state_mtx);
1580 session_ns->remote_state_data = NULL;
[a35b458]1581
[79ae36dd]1582 list_initialize(&session_ns->exch_list);
1583 fibril_mutex_initialize(&session_ns->mutex);
1584 atomic_set(&session_ns->refcnt, 0);
[450cd3a]1585}
[01ff41c]1586
[36c9234]1587/** Reply received callback.
[01ff41c]1588 *
[36c9234]1589 * This function is called whenever a reply for an asynchronous message sent out
1590 * by the asynchronous framework is received.
1591 *
1592 * Notify the fibril which is waiting for this message that it has arrived.
1593 *
[c07544d3]1594 * @param arg Pointer to the asynchronous message record.
1595 * @param retval Value returned in the answer.
1596 * @param data Call data of the answer.
[47b7006]1597 *
[01ff41c]1598 */
[b7fd2a0]1599void reply_received(void *arg, errno_t retval, ipc_call_t *data)
[01ff41c]1600{
[79ae36dd]1601 assert(arg);
[a35b458]1602
[9db9b10]1603 futex_down(&async_futex);
[a35b458]1604
[c07544d3]1605 amsg_t *msg = (amsg_t *) arg;
[01ff41c]1606 msg->retval = retval;
[a35b458]1607
[36c9234]1608 /* Copy data after futex_down, just in case the call was detached */
[9db9b10]1609 if ((msg->dataptr) && (data))
[c07544d3]1610 *msg->dataptr = *data;
[a35b458]1611
[c042bdd]1612 write_barrier();
[a35b458]1613
[c042bdd]1614 /* Remove message from timeout list */
[f53cc81]1615 if (msg->wdata.to_event.inlist)
1616 list_remove(&msg->wdata.to_event.link);
[a35b458]1617
[c07544d3]1618 msg->done = true;
[a35b458]1619
[47c9a8c]1620 if (msg->forget) {
1621 assert(msg->wdata.active);
1622 amsg_destroy(msg);
1623 } else if (!msg->wdata.active) {
[c07544d3]1624 msg->wdata.active = true;
[bc1f1c2]1625 fibril_add_ready(msg->wdata.fid);
[01ff41c]1626 }
[a35b458]1627
[01ff41c]1628 futex_up(&async_futex);
1629}
1630
[36c9234]1631/** Send message and return id of the sent message.
1632 *
1633 * The return value can be used as input for async_wait() to wait for
1634 * completion.
[01ff41c]1635 *
[79ae36dd]1636 * @param exch Exchange for sending the message.
1637 * @param imethod Service-defined interface and method.
[c07544d3]1638 * @param arg1 Service-defined payload argument.
1639 * @param arg2 Service-defined payload argument.
1640 * @param arg3 Service-defined payload argument.
1641 * @param arg4 Service-defined payload argument.
[01c3bb4]1642 * @param dataptr If non-NULL, storage where the reply data will be stored.
[c07544d3]1643 *
1644 * @return Hash of the sent message or 0 on error.
[36c9234]1645 *
[01ff41c]1646 */
[79ae36dd]1647aid_t async_send_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
[96b02eb9]1648 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
[01ff41c]1649{
[79ae36dd]1650 if (exch == NULL)
1651 return 0;
[a35b458]1652
[47c9a8c]1653 amsg_t *msg = amsg_create();
[79ae36dd]1654 if (msg == NULL)
[c07544d3]1655 return 0;
[a35b458]1656
[01ff41c]1657 msg->dataptr = dataptr;
[c07544d3]1658 msg->wdata.active = true;
[a35b458]1659
[79ae36dd]1660 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4, msg,
[dcc150cb]1661 reply_received);
[a35b458]1662
[01ff41c]1663 return (aid_t) msg;
1664}
1665
[90f5d64]1666/** Send message and return id of the sent message
1667 *
[36c9234]1668 * The return value can be used as input for async_wait() to wait for
1669 * completion.
1670 *
[79ae36dd]1671 * @param exch Exchange for sending the message.
1672 * @param imethod Service-defined interface and method.
[c07544d3]1673 * @param arg1 Service-defined payload argument.
1674 * @param arg2 Service-defined payload argument.
1675 * @param arg3 Service-defined payload argument.
1676 * @param arg4 Service-defined payload argument.
1677 * @param arg5 Service-defined payload argument.
1678 * @param dataptr If non-NULL, storage where the reply data will be
1679 * stored.
1680 *
1681 * @return Hash of the sent message or 0 on error.
[36c9234]1682 *
[90f5d64]1683 */
[79ae36dd]1684aid_t async_send_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
[96b02eb9]1685 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
[0cc4313]1686 ipc_call_t *dataptr)
[90f5d64]1687{
[79ae36dd]1688 if (exch == NULL)
1689 return 0;
[a35b458]1690
[47c9a8c]1691 amsg_t *msg = amsg_create();
[79ae36dd]1692 if (msg == NULL)
[c07544d3]1693 return 0;
[a35b458]1694
[90f5d64]1695 msg->dataptr = dataptr;
[c07544d3]1696 msg->wdata.active = true;
[a35b458]1697
[79ae36dd]1698 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4, arg5,
[dcc150cb]1699 msg, reply_received);
[a35b458]1700
[90f5d64]1701 return (aid_t) msg;
1702}
1703
[36c9234]1704/** Wait for a message sent by the async framework.
[01ff41c]1705 *
[c07544d3]1706 * @param amsgid Hash of the message to wait for.
1707 * @param retval Pointer to storage where the retval of the answer will
1708 * be stored.
1709 *
[01ff41c]1710 */
[b7fd2a0]1711void async_wait_for(aid_t amsgid, errno_t *retval)
[01ff41c]1712{
[79ae36dd]1713 assert(amsgid);
[a35b458]1714
[01ff41c]1715 amsg_t *msg = (amsg_t *) amsgid;
[a35b458]1716
[01ff41c]1717 futex_down(&async_futex);
[a35b458]1718
[47c9a8c]1719 assert(!msg->forget);
1720 assert(!msg->destroyed);
[a35b458]1721
[01ff41c]1722 if (msg->done) {
1723 futex_up(&async_futex);
1724 goto done;
1725 }
[a35b458]1726
[bc1f1c2]1727 msg->wdata.fid = fibril_get_id();
[c07544d3]1728 msg->wdata.active = false;
[f53cc81]1729 msg->wdata.to_event.inlist = false;
[a35b458]1730
[36c9234]1731 /* Leave the async_futex locked when entering this function */
[116d3f6f]1732 fibril_switch(FIBRIL_TO_MANAGER);
[a35b458]1733
[c07544d3]1734 /* Futex is up automatically after fibril_switch */
[a35b458]1735
[01ff41c]1736done:
1737 if (retval)
1738 *retval = msg->retval;
[a35b458]1739
[47c9a8c]1740 amsg_destroy(msg);
[01ff41c]1741}
[0b99e40]1742
[36c9234]1743/** Wait for a message sent by the async framework, timeout variant.
[47c9a8c]1744 *
1745 * If the wait times out, the caller may choose to either wait again by calling
1746 * async_wait_for() or async_wait_timeout(), or forget the message via
1747 * async_forget().
[c042bdd]1748 *
[c07544d3]1749 * @param amsgid Hash of the message to wait for.
1750 * @param retval Pointer to storage where the retval of the answer will
1751 * be stored.
1752 * @param timeout Timeout in microseconds.
1753 *
1754 * @return Zero on success, ETIMEOUT if the timeout has expired.
[c042bdd]1755 *
1756 */
[b7fd2a0]1757errno_t async_wait_timeout(aid_t amsgid, errno_t *retval, suseconds_t timeout)
[c042bdd]1758{
[79ae36dd]1759 assert(amsgid);
[a35b458]1760
[c042bdd]1761 amsg_t *msg = (amsg_t *) amsgid;
[a35b458]1762
[c042bdd]1763 futex_down(&async_futex);
[a35b458]1764
[47c9a8c]1765 assert(!msg->forget);
1766 assert(!msg->destroyed);
[a35b458]1767
[c042bdd]1768 if (msg->done) {
1769 futex_up(&async_futex);
1770 goto done;
1771 }
[a35b458]1772
[1db6dfd]1773 /*
1774 * Negative timeout is converted to zero timeout to avoid
1775 * using tv_add with negative augmenter.
1776 */
1777 if (timeout < 0)
1778 timeout = 0;
[a35b458]1779
[45cbcaf4]1780 getuptime(&msg->wdata.to_event.expires);
[7f9d97f3]1781 tv_add_diff(&msg->wdata.to_event.expires, timeout);
[a35b458]1782
[1db6dfd]1783 /*
1784 * Current fibril is inserted as waiting regardless of the
1785 * "size" of the timeout.
1786 *
1787 * Checking for msg->done and immediately bailing out when
1788 * timeout == 0 would mean that the manager fibril would never
1789 * run (consider single threaded program).
1790 * Thus the IPC answer would be never retrieved from the kernel.
1791 *
1792 * Notice that the actual delay would be very small because we
1793 * - switch to manager fibril
1794 * - the manager sees expired timeout
1795 * - and thus adds us back to ready queue
1796 * - manager switches back to some ready fibril
1797 * (prior it, it checks for incoming IPC).
1798 *
1799 */
[bc1f1c2]1800 msg->wdata.fid = fibril_get_id();
[c07544d3]1801 msg->wdata.active = false;
[b6ee5b1]1802 async_insert_timeout(&msg->wdata);
[a35b458]1803
[36c9234]1804 /* Leave the async_futex locked when entering this function */
[116d3f6f]1805 fibril_switch(FIBRIL_TO_MANAGER);
[a35b458]1806
[c07544d3]1807 /* Futex is up automatically after fibril_switch */
[a35b458]1808
[c042bdd]1809 if (!msg->done)
1810 return ETIMEOUT;
[a35b458]1811
[c042bdd]1812done:
1813 if (retval)
1814 *retval = msg->retval;
[a35b458]1815
[47c9a8c]1816 amsg_destroy(msg);
[a35b458]1817
[c042bdd]1818 return 0;
1819}
[a35b458]1820
[47c9a8c]1821/** Discard the message / reply on arrival.
1822 *
1823 * The message will be marked to be discarded once the reply arrives in
1824 * reply_received(). It is not allowed to call async_wait_for() or
1825 * async_wait_timeout() on this message after a call to this function.
1826 *
1827 * @param amsgid Hash of the message to forget.
1828 */
1829void async_forget(aid_t amsgid)
1830{
1831 amsg_t *msg = (amsg_t *) amsgid;
[a35b458]1832
[47c9a8c]1833 assert(msg);
1834 assert(!msg->forget);
1835 assert(!msg->destroyed);
[a35b458]1836
[47c9a8c]1837 futex_down(&async_futex);
[a35b458]1838
[375e501]1839 if (msg->done) {
[47c9a8c]1840 amsg_destroy(msg);
[375e501]1841 } else {
1842 msg->dataptr = NULL;
[47c9a8c]1843 msg->forget = true;
[375e501]1844 }
[a35b458]1845
[47c9a8c]1846 futex_up(&async_futex);
1847}
[0b99e40]1848
[36c9234]1849/** Wait for specified time.
[44c6d88d]1850 *
[36c9234]1851 * The current fibril is suspended but the thread continues to execute.
1852 *
[c07544d3]1853 * @param timeout Duration of the wait in microseconds.
1854 *
[44c6d88d]1855 */
1856void async_usleep(suseconds_t timeout)
1857{
[6a5d05b]1858 awaiter_t awaiter;
1859 awaiter_initialize(&awaiter);
[a35b458]1860
[6a5d05b]1861 awaiter.fid = fibril_get_id();
[a35b458]1862
[6a5d05b]1863 getuptime(&awaiter.to_event.expires);
1864 tv_add_diff(&awaiter.to_event.expires, timeout);
[a35b458]1865
[44c6d88d]1866 futex_down(&async_futex);
[a35b458]1867
[6a5d05b]1868 async_insert_timeout(&awaiter);
[a35b458]1869
[36c9234]1870 /* Leave the async_futex locked when entering this function */
[116d3f6f]1871 fibril_switch(FIBRIL_TO_MANAGER);
[a35b458]1872
[c07544d3]1873 /* Futex is up automatically after fibril_switch() */
[44c6d88d]1874}
[da0c91e7]1875
[39026d7c]1876/** Delay execution for the specified number of seconds
1877 *
1878 * @param sec Number of seconds to sleep
1879 */
1880void async_sleep(unsigned int sec)
1881{
1882 /*
1883 * Sleep in 1000 second steps to support
1884 * full argument range
1885 */
1886
1887 while (sec > 0) {
1888 unsigned int period = (sec > 1000) ? 1000 : sec;
1889
1890 async_usleep(period * 1000000);
1891 sec -= period;
1892 }
1893}
1894
[0cc4313]1895/** Pseudo-synchronous message sending - fast version.
1896 *
1897 * Send message asynchronously and return only after the reply arrives.
1898 *
1899 * This function can only transfer 4 register payload arguments. For
1900 * transferring more arguments, see the slower async_req_slow().
1901 *
[79ae36dd]1902 * @param exch Exchange for sending the message.
1903 * @param imethod Interface and method of the call.
[c07544d3]1904 * @param arg1 Service-defined payload argument.
1905 * @param arg2 Service-defined payload argument.
1906 * @param arg3 Service-defined payload argument.
1907 * @param arg4 Service-defined payload argument.
1908 * @param r1 If non-NULL, storage for the 1st reply argument.
1909 * @param r2 If non-NULL, storage for the 2nd reply argument.
1910 * @param r3 If non-NULL, storage for the 3rd reply argument.
1911 * @param r4 If non-NULL, storage for the 4th reply argument.
1912 * @param r5 If non-NULL, storage for the 5th reply argument.
1913 *
[cde999a]1914 * @return Return code of the reply or an error code.
[c07544d3]1915 *
[0cc4313]1916 */
[b7fd2a0]1917errno_t async_req_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
[96b02eb9]1918 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
1919 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
[085bd54]1920{
[79ae36dd]1921 if (exch == NULL)
1922 return ENOENT;
[a35b458]1923
[0cc4313]1924 ipc_call_t result;
[79ae36dd]1925 aid_t aid = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
[0cc4313]1926 &result);
[a35b458]1927
[b7fd2a0]1928 errno_t rc;
[79ae36dd]1929 async_wait_for(aid, &rc);
[a35b458]1930
[c07544d3]1931 if (r1)
[0cc4313]1932 *r1 = IPC_GET_ARG1(result);
[a35b458]1933
[0cc4313]1934 if (r2)
1935 *r2 = IPC_GET_ARG2(result);
[a35b458]1936
[0cc4313]1937 if (r3)
1938 *r3 = IPC_GET_ARG3(result);
[a35b458]1939
[0cc4313]1940 if (r4)
1941 *r4 = IPC_GET_ARG4(result);
[a35b458]1942
[0cc4313]1943 if (r5)
1944 *r5 = IPC_GET_ARG5(result);
[a35b458]1945
[0cc4313]1946 return rc;
[085bd54]1947}
1948
[0cc4313]1949/** Pseudo-synchronous message sending - slow version.
1950 *
1951 * Send message asynchronously and return only after the reply arrives.
1952 *
[79ae36dd]1953 * @param exch Exchange for sending the message.
1954 * @param imethod Interface and method of the call.
[c07544d3]1955 * @param arg1 Service-defined payload argument.
1956 * @param arg2 Service-defined payload argument.
1957 * @param arg3 Service-defined payload argument.
1958 * @param arg4 Service-defined payload argument.
1959 * @param arg5 Service-defined payload argument.
1960 * @param r1 If non-NULL, storage for the 1st reply argument.
1961 * @param r2 If non-NULL, storage for the 2nd reply argument.
1962 * @param r3 If non-NULL, storage for the 3rd reply argument.
1963 * @param r4 If non-NULL, storage for the 4th reply argument.
1964 * @param r5 If non-NULL, storage for the 5th reply argument.
1965 *
[cde999a]1966 * @return Return code of the reply or an error code.
[c07544d3]1967 *
[0cc4313]1968 */
[b7fd2a0]1969errno_t async_req_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
[96b02eb9]1970 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
1971 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
[085bd54]1972{
[79ae36dd]1973 if (exch == NULL)
1974 return ENOENT;
[a35b458]1975
[0cc4313]1976 ipc_call_t result;
[79ae36dd]1977 aid_t aid = async_send_5(exch, imethod, arg1, arg2, arg3, arg4, arg5,
[0cc4313]1978 &result);
[a35b458]1979
[b7fd2a0]1980 errno_t rc;
[79ae36dd]1981 async_wait_for(aid, &rc);
[a35b458]1982
[c07544d3]1983 if (r1)
[0cc4313]1984 *r1 = IPC_GET_ARG1(result);
[a35b458]1985
[0cc4313]1986 if (r2)
1987 *r2 = IPC_GET_ARG2(result);
[a35b458]1988
[0cc4313]1989 if (r3)
1990 *r3 = IPC_GET_ARG3(result);
[a35b458]1991
[0cc4313]1992 if (r4)
1993 *r4 = IPC_GET_ARG4(result);
[a35b458]1994
[0cc4313]1995 if (r5)
1996 *r5 = IPC_GET_ARG5(result);
[a35b458]1997
[0cc4313]1998 return rc;
[085bd54]1999}
[b2951e2]2000
[79ae36dd]2001void async_msg_0(async_exch_t *exch, sysarg_t imethod)
[64d2b10]2002{
[79ae36dd]2003 if (exch != NULL)
[dcc150cb]2004 ipc_call_async_0(exch->phone, imethod, NULL, NULL);
[64d2b10]2005}
2006
[79ae36dd]2007void async_msg_1(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1)
[64d2b10]2008{
[79ae36dd]2009 if (exch != NULL)
[dcc150cb]2010 ipc_call_async_1(exch->phone, imethod, arg1, NULL, NULL);
[64d2b10]2011}
2012
[79ae36dd]2013void async_msg_2(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2014 sysarg_t arg2)
[64d2b10]2015{
[79ae36dd]2016 if (exch != NULL)
[dcc150cb]2017 ipc_call_async_2(exch->phone, imethod, arg1, arg2, NULL, NULL);
[64d2b10]2018}
2019
[79ae36dd]2020void async_msg_3(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2021 sysarg_t arg2, sysarg_t arg3)
[64d2b10]2022{
[79ae36dd]2023 if (exch != NULL)
2024 ipc_call_async_3(exch->phone, imethod, arg1, arg2, arg3, NULL,
[dcc150cb]2025 NULL);
[64d2b10]2026}
2027
[79ae36dd]2028void async_msg_4(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2029 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
[64d2b10]2030{
[79ae36dd]2031 if (exch != NULL)
2032 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4,
[dcc150cb]2033 NULL, NULL);
[64d2b10]2034}
2035
[79ae36dd]2036void async_msg_5(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2037 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
[64d2b10]2038{
[79ae36dd]2039 if (exch != NULL)
2040 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4,
[dcc150cb]2041 arg5, NULL, NULL);
[64d2b10]2042}
2043
[b7fd2a0]2044errno_t async_answer_0(cap_handle_t chandle, errno_t retval)
[64d2b10]2045{
[01c3bb4]2046 return ipc_answer_0(chandle, retval);
[64d2b10]2047}
2048
[b7fd2a0]2049errno_t async_answer_1(cap_handle_t chandle, errno_t retval, sysarg_t arg1)
[64d2b10]2050{
[01c3bb4]2051 return ipc_answer_1(chandle, retval, arg1);
[64d2b10]2052}
2053
[b7fd2a0]2054errno_t async_answer_2(cap_handle_t chandle, errno_t retval, sysarg_t arg1,
[64d2b10]2055 sysarg_t arg2)
2056{
[01c3bb4]2057 return ipc_answer_2(chandle, retval, arg1, arg2);
[64d2b10]2058}
2059
[b7fd2a0]2060errno_t async_answer_3(cap_handle_t chandle, errno_t retval, sysarg_t arg1,
[64d2b10]2061 sysarg_t arg2, sysarg_t arg3)
2062{
[01c3bb4]2063 return ipc_answer_3(chandle, retval, arg1, arg2, arg3);
[64d2b10]2064}
2065
[b7fd2a0]2066errno_t async_answer_4(cap_handle_t chandle, errno_t retval, sysarg_t arg1,
[64d2b10]2067 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
2068{
[01c3bb4]2069 return ipc_answer_4(chandle, retval, arg1, arg2, arg3, arg4);
[64d2b10]2070}
2071
[b7fd2a0]2072errno_t async_answer_5(cap_handle_t chandle, errno_t retval, sysarg_t arg1,
[64d2b10]2073 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
2074{
[01c3bb4]2075 return ipc_answer_5(chandle, retval, arg1, arg2, arg3, arg4, arg5);
[64d2b10]2076}
2077
[b7fd2a0]2078errno_t async_forward_fast(cap_handle_t chandle, async_exch_t *exch,
[79ae36dd]2079 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
[64d2b10]2080{
[79ae36dd]2081 if (exch == NULL)
2082 return ENOENT;
[a35b458]2083
[01c3bb4]2084 return ipc_forward_fast(chandle, exch->phone, imethod, arg1, arg2, mode);
[64d2b10]2085}
2086
[b7fd2a0]2087errno_t async_forward_slow(cap_handle_t chandle, async_exch_t *exch,
[79ae36dd]2088 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
2089 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
[64d2b10]2090{
[79ae36dd]2091 if (exch == NULL)
2092 return ENOENT;
[a35b458]2093
[01c3bb4]2094 return ipc_forward_slow(chandle, exch->phone, imethod, arg1, arg2, arg3,
[79ae36dd]2095 arg4, arg5, mode);
[64d2b10]2096}
2097
[007e6efa]2098/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
2099 *
2100 * Ask through phone for a new connection to some service.
2101 *
[79ae36dd]2102 * @param exch Exchange for sending the message.
[007e6efa]2103 * @param arg1 User defined argument.
2104 * @param arg2 User defined argument.
2105 * @param arg3 User defined argument.
2106 *
[cde999a]2107 * @return Zero on success or an error code.
[007e6efa]2108 *
2109 */
[b7fd2a0]2110errno_t async_connect_to_me(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
[f9b2cb4c]2111 sysarg_t arg3)
[007e6efa]2112{
[79ae36dd]2113 if (exch == NULL)
2114 return ENOENT;
[a35b458]2115
[ab34cc9]2116 ipc_call_t answer;
[f9b2cb4c]2117 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
[ab34cc9]2118 &answer);
[a35b458]2119
[b7fd2a0]2120 errno_t rc;
[ab34cc9]2121 async_wait_for(req, &rc);
[007e6efa]2122 if (rc != EOK)
[b7fd2a0]2123 return (errno_t) rc;
[a35b458]2124
[007e6efa]2125 return EOK;
2126}
2127
[b7fd2a0]2128static errno_t async_connect_me_to_internal(int phone, sysarg_t arg1, sysarg_t arg2,
[a99cbc1e]2129 sysarg_t arg3, sysarg_t arg4, int *out_phone)
[f74392f]2130{
[79ae36dd]2131 ipc_call_t result;
[a35b458]2132
[a99cbc1e]2133 // XXX: Workaround for GCC's inability to infer association between
2134 // rc == EOK and *out_phone being assigned.
2135 *out_phone = -1;
[a35b458]2136
[47c9a8c]2137 amsg_t *msg = amsg_create();
2138 if (!msg)
[79ae36dd]2139 return ENOENT;
[a35b458]2140
[79ae36dd]2141 msg->dataptr = &result;
2142 msg->wdata.active = true;
[a35b458]2143
[79ae36dd]2144 ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, arg4,
[dcc150cb]2145 msg, reply_received);
[a35b458]2146
[b7fd2a0]2147 errno_t rc;
[79ae36dd]2148 async_wait_for((aid_t) msg, &rc);
[a35b458]2149
[007e6efa]2150 if (rc != EOK)
[f74392f]2151 return rc;
[a35b458]2152
[a99cbc1e]2153 *out_phone = (int) IPC_GET_ARG5(result);
2154 return EOK;
[79ae36dd]2155}
2156
2157/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2158 *
2159 * Ask through for a new connection to some service.
2160 *
2161 * @param mgmt Exchange management style.
2162 * @param exch Exchange for sending the message.
2163 * @param arg1 User defined argument.
2164 * @param arg2 User defined argument.
2165 * @param arg3 User defined argument.
2166 *
2167 * @return New session on success or NULL on error.
2168 *
2169 */
2170async_sess_t *async_connect_me_to(exch_mgmt_t mgmt, async_exch_t *exch,
2171 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
2172{
2173 if (exch == NULL) {
2174 errno = ENOENT;
2175 return NULL;
2176 }
[a35b458]2177
[79ae36dd]2178 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2179 if (sess == NULL) {
2180 errno = ENOMEM;
2181 return NULL;
2182 }
[a35b458]2183
[a99cbc1e]2184 int phone;
[b7fd2a0]2185 errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
[a99cbc1e]2186 0, &phone);
2187 if (rc != EOK) {
2188 errno = rc;
[79ae36dd]2189 free(sess);
2190 return NULL;
2191 }
[a35b458]2192
[566992e1]2193 sess->iface = 0;
[79ae36dd]2194 sess->mgmt = mgmt;
2195 sess->phone = phone;
2196 sess->arg1 = arg1;
2197 sess->arg2 = arg2;
2198 sess->arg3 = arg3;
[a35b458]2199
[58cbf8d5]2200 fibril_mutex_initialize(&sess->remote_state_mtx);
2201 sess->remote_state_data = NULL;
[a35b458]2202
[79ae36dd]2203 list_initialize(&sess->exch_list);
2204 fibril_mutex_initialize(&sess->mutex);
2205 atomic_set(&sess->refcnt, 0);
[a35b458]2206
[79ae36dd]2207 return sess;
[f74392f]2208}
2209
[0dd16778]2210/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2211 *
2212 * Ask through phone for a new connection to some service and block until
2213 * success.
2214 *
2215 * @param exch Exchange for sending the message.
2216 * @param iface Connection interface.
2217 * @param arg2 User defined argument.
2218 * @param arg3 User defined argument.
2219 *
2220 * @return New session on success or NULL on error.
2221 *
2222 */
2223async_sess_t *async_connect_me_to_iface(async_exch_t *exch, iface_t iface,
2224 sysarg_t arg2, sysarg_t arg3)
2225{
2226 if (exch == NULL) {
2227 errno = ENOENT;
2228 return NULL;
2229 }
[a35b458]2230
[0dd16778]2231 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2232 if (sess == NULL) {
2233 errno = ENOMEM;
2234 return NULL;
2235 }
[a35b458]2236
[a99cbc1e]2237 int phone;
[b7fd2a0]2238 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
[a99cbc1e]2239 arg3, 0, &phone);
2240 if (rc != EOK) {
2241 errno = rc;
[0dd16778]2242 free(sess);
2243 return NULL;
2244 }
[a35b458]2245
[0dd16778]2246 sess->iface = iface;
2247 sess->phone = phone;
2248 sess->arg1 = iface;
2249 sess->arg2 = arg2;
2250 sess->arg3 = arg3;
[a35b458]2251
[0dd16778]2252 fibril_mutex_initialize(&sess->remote_state_mtx);
2253 sess->remote_state_data = NULL;
[a35b458]2254
[0dd16778]2255 list_initialize(&sess->exch_list);
2256 fibril_mutex_initialize(&sess->mutex);
2257 atomic_set(&sess->refcnt, 0);
[a35b458]2258
[0dd16778]2259 return sess;
2260}
2261
[93ad49a8]2262/** Set arguments for new connections.
[0f4532e]2263 *
2264 * FIXME This is an ugly hack to work around the problem that parallel
2265 * exchanges are implemented using parallel connections. When we create
[93ad49a8]2266 * a callback session, the framework does not know arguments for the new
2267 * connections.
[0f4532e]2268 *
2269 * The proper solution seems to be to implement parallel exchanges using
2270 * tagging.
2271 */
[93ad49a8]2272void async_sess_args_set(async_sess_t *sess, sysarg_t arg1, sysarg_t arg2,
2273 sysarg_t arg3)
[0f4532e]2274{
[93ad49a8]2275 sess->arg1 = arg1;
2276 sess->arg2 = arg2;
2277 sess->arg3 = arg3;
[0f4532e]2278}
2279
[f74392f]2280/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
[007e6efa]2281 *
[f74392f]2282 * Ask through phone for a new connection to some service and block until
2283 * success.
2284 *
[79ae36dd]2285 * @param mgmt Exchange management style.
2286 * @param exch Exchange for sending the message.
2287 * @param arg1 User defined argument.
2288 * @param arg2 User defined argument.
2289 * @param arg3 User defined argument.
[007e6efa]2290 *
[79ae36dd]2291 * @return New session on success or NULL on error.
[f74392f]2292 *
2293 */
[79ae36dd]2294async_sess_t *async_connect_me_to_blocking(exch_mgmt_t mgmt, async_exch_t *exch,
2295 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
[f74392f]2296{
[79ae36dd]2297 if (exch == NULL) {
2298 errno = ENOENT;
2299 return NULL;
2300 }
[a35b458]2301
[79ae36dd]2302 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2303 if (sess == NULL) {
2304 errno = ENOMEM;
2305 return NULL;
2306 }
[a35b458]2307
[a99cbc1e]2308 int phone;
[b7fd2a0]2309 errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
[a99cbc1e]2310 IPC_FLAG_BLOCKING, &phone);
[a35b458]2311
[a99cbc1e]2312 if (rc != EOK) {
2313 errno = rc;
[79ae36dd]2314 free(sess);
2315 return NULL;
2316 }
[a35b458]2317
[566992e1]2318 sess->iface = 0;
[79ae36dd]2319 sess->mgmt = mgmt;
2320 sess->phone = phone;
2321 sess->arg1 = arg1;
2322 sess->arg2 = arg2;
2323 sess->arg3 = arg3;
[a35b458]2324
[58cbf8d5]2325 fibril_mutex_initialize(&sess->remote_state_mtx);
2326 sess->remote_state_data = NULL;
[a35b458]2327
[79ae36dd]2328 list_initialize(&sess->exch_list);
2329 fibril_mutex_initialize(&sess->mutex);
2330 atomic_set(&sess->refcnt, 0);
[a35b458]2331
[79ae36dd]2332 return sess;
[f74392f]2333}
2334
[566992e1]2335/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2336 *
2337 * Ask through phone for a new connection to some service and block until
2338 * success.
2339 *
2340 * @param exch Exchange for sending the message.
2341 * @param iface Connection interface.
2342 * @param arg2 User defined argument.
2343 * @param arg3 User defined argument.
2344 *
2345 * @return New session on success or NULL on error.
2346 *
2347 */
2348async_sess_t *async_connect_me_to_blocking_iface(async_exch_t *exch, iface_t iface,
2349 sysarg_t arg2, sysarg_t arg3)
2350{
2351 if (exch == NULL) {
2352 errno = ENOENT;
2353 return NULL;
2354 }
[a35b458]2355
[566992e1]2356 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2357 if (sess == NULL) {
2358 errno = ENOMEM;
2359 return NULL;
2360 }
[a35b458]2361
[a99cbc1e]2362 int phone;
[b7fd2a0]2363 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
[a99cbc1e]2364 arg3, IPC_FLAG_BLOCKING, &phone);
2365 if (rc != EOK) {
2366 errno = rc;
[566992e1]2367 free(sess);
2368 return NULL;
2369 }
[a35b458]2370
[566992e1]2371 sess->iface = iface;
2372 sess->phone = phone;
2373 sess->arg1 = iface;
2374 sess->arg2 = arg2;
2375 sess->arg3 = arg3;
[a35b458]2376
[566992e1]2377 fibril_mutex_initialize(&sess->remote_state_mtx);
2378 sess->remote_state_data = NULL;
[a35b458]2379
[566992e1]2380 list_initialize(&sess->exch_list);
2381 fibril_mutex_initialize(&sess->mutex);
2382 atomic_set(&sess->refcnt, 0);
[a35b458]2383
[566992e1]2384 return sess;
2385}
2386
[64d2b10]2387/** Connect to a task specified by id.
2388 *
2389 */
[79ae36dd]2390async_sess_t *async_connect_kbox(task_id_t id)
[64d2b10]2391{
[79ae36dd]2392 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2393 if (sess == NULL) {
2394 errno = ENOMEM;
2395 return NULL;
2396 }
[a35b458]2397
[569a51a]2398 cap_handle_t phone;
[b7fd2a0]2399 errno_t rc = ipc_connect_kbox(id, &phone);
[569a51a]2400 if (rc != EOK) {
2401 errno = rc;
[79ae36dd]2402 free(sess);
2403 return NULL;
2404 }
[a35b458]2405
[566992e1]2406 sess->iface = 0;
[79ae36dd]2407 sess->mgmt = EXCHANGE_ATOMIC;
2408 sess->phone = phone;
2409 sess->arg1 = 0;
2410 sess->arg2 = 0;
2411 sess->arg3 = 0;
[a35b458]2412
[58cbf8d5]2413 fibril_mutex_initialize(&sess->remote_state_mtx);
2414 sess->remote_state_data = NULL;
[a35b458]2415
[79ae36dd]2416 list_initialize(&sess->exch_list);
2417 fibril_mutex_initialize(&sess->mutex);
2418 atomic_set(&sess->refcnt, 0);
[a35b458]2419
[79ae36dd]2420 return sess;
2421}
2422
[b7fd2a0]2423static errno_t async_hangup_internal(int phone)
[79ae36dd]2424{
2425 return ipc_hangup(phone);
[64d2b10]2426}
2427
2428/** Wrapper for ipc_hangup.
2429 *
[79ae36dd]2430 * @param sess Session to hung up.
[64d2b10]2431 *
[cde999a]2432 * @return Zero on success or an error code.
[64d2b10]2433 *
2434 */
[b7fd2a0]2435errno_t async_hangup(async_sess_t *sess)
[64d2b10]2436{
[36e2b55]2437 async_exch_t *exch;
[a35b458]2438
[79ae36dd]2439 assert(sess);
[a35b458]2440
[79ae36dd]2441 if (atomic_get(&sess->refcnt) > 0)
2442 return EBUSY;
[a35b458]2443
[36e2b55]2444 fibril_mutex_lock(&async_sess_mutex);
[a35b458]2445
[b7fd2a0]2446 errno_t rc = async_hangup_internal(sess->phone);
[a35b458]2447
[36e2b55]2448 while (!list_empty(&sess->exch_list)) {
2449 exch = (async_exch_t *)
2450 list_get_instance(list_first(&sess->exch_list),
2451 async_exch_t, sess_link);
[a35b458]2452
[36e2b55]2453 list_remove(&exch->sess_link);
2454 list_remove(&exch->global_link);
2455 async_hangup_internal(exch->phone);
2456 free(exch);
2457 }
[4c50c8d]2458
2459 free(sess);
[a35b458]2460
[36e2b55]2461 fibril_mutex_unlock(&async_sess_mutex);
[a35b458]2462
[79ae36dd]2463 return rc;
[64d2b10]2464}
2465
2466/** Interrupt one thread of this task from waiting for IPC. */
2467void async_poke(void)
2468{
2469 ipc_poke();
2470}
2471
[79ae36dd]2472/** Start new exchange in a session.
2473 *
2474 * @param session Session.
2475 *
2476 * @return New exchange or NULL on error.
2477 *
2478 */
2479async_exch_t *async_exchange_begin(async_sess_t *sess)
2480{
2481 if (sess == NULL)
2482 return NULL;
[a35b458]2483
[566992e1]2484 exch_mgmt_t mgmt = sess->mgmt;
2485 if (sess->iface != 0)
2486 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
[a35b458]2487
[566992e1]2488 async_exch_t *exch = NULL;
[a35b458]2489
[79ae36dd]2490 fibril_mutex_lock(&async_sess_mutex);
[a35b458]2491
[79ae36dd]2492 if (!list_empty(&sess->exch_list)) {
2493 /*
2494 * There are inactive exchanges in the session.
2495 */
2496 exch = (async_exch_t *)
[b72efe8]2497 list_get_instance(list_first(&sess->exch_list),
2498 async_exch_t, sess_link);
[a35b458]2499
[79ae36dd]2500 list_remove(&exch->sess_link);
2501 list_remove(&exch->global_link);
2502 } else {
2503 /*
2504 * There are no available exchanges in the session.
2505 */
[a35b458]2506
[566992e1]2507 if ((mgmt == EXCHANGE_ATOMIC) ||
2508 (mgmt == EXCHANGE_SERIALIZE)) {
[79ae36dd]2509 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
2510 if (exch != NULL) {
[b72efe8]2511 link_initialize(&exch->sess_link);
2512 link_initialize(&exch->global_link);
[79ae36dd]2513 exch->sess = sess;
2514 exch->phone = sess->phone;
2515 }
[566992e1]2516 } else if (mgmt == EXCHANGE_PARALLEL) {
2517 int phone;
[b7fd2a0]2518 errno_t rc;
[a35b458]2519
[566992e1]2520 retry:
[79ae36dd]2521 /*
2522 * Make a one-time attempt to connect a new data phone.
2523 */
[a99cbc1e]2524 rc = async_connect_me_to_internal(sess->phone, sess->arg1,
2525 sess->arg2, sess->arg3, 0, &phone);
2526 if (rc == EOK) {
[79ae36dd]2527 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
2528 if (exch != NULL) {
[b72efe8]2529 link_initialize(&exch->sess_link);
2530 link_initialize(&exch->global_link);
[79ae36dd]2531 exch->sess = sess;
2532 exch->phone = phone;
2533 } else
2534 async_hangup_internal(phone);
2535 } else if (!list_empty(&inactive_exch_list)) {
2536 /*
2537 * We did not manage to connect a new phone. But we
2538 * can try to close some of the currently inactive
2539 * connections in other sessions and try again.
2540 */
2541 exch = (async_exch_t *)
[b72efe8]2542 list_get_instance(list_first(&inactive_exch_list),
2543 async_exch_t, global_link);
[a35b458]2544
[79ae36dd]2545 list_remove(&exch->sess_link);
2546 list_remove(&exch->global_link);
2547 async_hangup_internal(exch->phone);
2548 free(exch);
2549 goto retry;
2550 } else {
2551 /*
2552 * Wait for a phone to become available.
2553 */
2554 fibril_condvar_wait(&avail_phone_cv, &async_sess_mutex);
2555 goto retry;
2556 }
2557 }
2558 }
[a35b458]2559
[79ae36dd]2560 fibril_mutex_unlock(&async_sess_mutex);
[a35b458]2561
[79ae36dd]2562 if (exch != NULL) {
2563 atomic_inc(&sess->refcnt);
[a35b458]2564
[566992e1]2565 if (mgmt == EXCHANGE_SERIALIZE)
[79ae36dd]2566 fibril_mutex_lock(&sess->mutex);
2567 }
[a35b458]2568
[79ae36dd]2569 return exch;
2570}
2571
2572/** Finish an exchange.
2573 *
2574 * @param exch Exchange to finish.
2575 *
2576 */
2577void async_exchange_end(async_exch_t *exch)
2578{
2579 if (exch == NULL)
2580 return;
[a35b458]2581
[79ae36dd]2582 async_sess_t *sess = exch->sess;
[3ca2e36]2583 assert(sess != NULL);
[a35b458]2584
[566992e1]2585 exch_mgmt_t mgmt = sess->mgmt;
2586 if (sess->iface != 0)
2587 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
[a35b458]2588
[1c6436a]2589 atomic_dec(&sess->refcnt);
[a35b458]2590
[566992e1]2591 if (mgmt == EXCHANGE_SERIALIZE)
[79ae36dd]2592 fibril_mutex_unlock(&sess->mutex);
[a35b458]2593
[79ae36dd]2594 fibril_mutex_lock(&async_sess_mutex);
[a35b458]2595
[79ae36dd]2596 list_append(&exch->sess_link, &sess->exch_list);
2597 list_append(&exch->global_link, &inactive_exch_list);
2598 fibril_condvar_signal(&avail_phone_cv);
[a35b458]2599
[79ae36dd]2600 fibril_mutex_unlock(&async_sess_mutex);
2601}
2602
[47b7006]2603/** Wrapper for IPC_M_SHARE_IN calls using the async framework.
2604 *
[79ae36dd]2605 * @param exch Exchange for sending the message.
2606 * @param size Size of the destination address space area.
2607 * @param arg User defined argument.
2608 * @param flags Storage for the received flags. Can be NULL.
[df956b9b]2609 * @param dst Address of the storage for the destination address space area
2610 * base address. Cannot be NULL.
[0da4e41]2611 *
[cde999a]2612 * @return Zero on success or an error code from errno.h.
[0da4e41]2613 *
2614 */
[b7fd2a0]2615errno_t async_share_in_start(async_exch_t *exch, size_t size, sysarg_t arg,
[fbcdeb8]2616 unsigned int *flags, void **dst)
[0da4e41]2617{
[79ae36dd]2618 if (exch == NULL)
2619 return ENOENT;
[a35b458]2620
[fbcdeb8]2621 sysarg_t _flags = 0;
2622 sysarg_t _dst = (sysarg_t) -1;
[b7fd2a0]2623 errno_t res = async_req_2_4(exch, IPC_M_SHARE_IN, (sysarg_t) size,
[fbcdeb8]2624 arg, NULL, &_flags, NULL, &_dst);
[a35b458]2625
[0da4e41]2626 if (flags)
[fbcdeb8]2627 *flags = (unsigned int) _flags;
[a35b458]2628
[fbcdeb8]2629 *dst = (void *) _dst;
[0da4e41]2630 return res;
2631}
2632
2633/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
2634 *
[47b7006]2635 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN
2636 * calls so that the user doesn't have to remember the meaning of each IPC
2637 * argument.
[0da4e41]2638 *
2639 * So far, this wrapper is to be used from within a connection fibril.
2640 *
[01c3bb4]2641 * @param chandle Storage for the handle of the IPC_M_SHARE_IN call.
2642 * @param size Destination address space area size.
[47b7006]2643 *
2644 * @return True on success, false on failure.
[0da4e41]2645 *
2646 */
[01c3bb4]2647bool async_share_in_receive(cap_handle_t *chandle, size_t *size)
[0da4e41]2648{
[01c3bb4]2649 assert(chandle);
[0da4e41]2650 assert(size);
[a35b458]2651
[47b7006]2652 ipc_call_t data;
[01c3bb4]2653 *chandle = async_get_call(&data);
[a35b458]2654
[228e490]2655 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
[47b7006]2656 return false;
[a35b458]2657
[fbcdeb8]2658 *size = (size_t) IPC_GET_ARG1(data);
[47b7006]2659 return true;
[0da4e41]2660}
2661
2662/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
2663 *
[fbcdeb8]2664 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
[47b7006]2665 * calls so that the user doesn't have to remember the meaning of each IPC
2666 * argument.
[0da4e41]2667 *
[01c3bb4]2668 * @param chandle Handle of the IPC_M_DATA_READ call to answer.
2669 * @param src Source address space base.
2670 * @param flags Flags to be used for sharing. Bits can be only cleared.
[47b7006]2671 *
2672 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]2673 *
2674 */
[b7fd2a0]2675errno_t async_share_in_finalize(cap_handle_t chandle, void *src, unsigned int flags)
[0da4e41]2676{
[01c3bb4]2677 return ipc_answer_3(chandle, EOK, (sysarg_t) src, (sysarg_t) flags,
[d7978525]2678 (sysarg_t) __entry);
[0da4e41]2679}
2680
[47b7006]2681/** Wrapper for IPC_M_SHARE_OUT calls using the async framework.
[0da4e41]2682 *
[79ae36dd]2683 * @param exch Exchange for sending the message.
2684 * @param src Source address space area base address.
2685 * @param flags Flags to be used for sharing. Bits can be only cleared.
[47b7006]2686 *
[cde999a]2687 * @return Zero on success or an error code from errno.h.
[0da4e41]2688 *
2689 */
[b7fd2a0]2690errno_t async_share_out_start(async_exch_t *exch, void *src, unsigned int flags)
[0da4e41]2691{
[79ae36dd]2692 if (exch == NULL)
2693 return ENOENT;
[a35b458]2694
[79ae36dd]2695 return async_req_3_0(exch, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
[96b02eb9]2696 (sysarg_t) flags);
[0da4e41]2697}
2698
2699/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
2700 *
[47b7006]2701 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT
2702 * calls so that the user doesn't have to remember the meaning of each IPC
2703 * argument.
[0da4e41]2704 *
2705 * So far, this wrapper is to be used from within a connection fibril.
2706 *
[01c3bb4]2707 * @param chandle Storage for the hash of the IPC_M_SHARE_OUT call.
2708 * @param size Storage for the source address space area size.
2709 * @param flags Storage for the sharing flags.
[47b7006]2710 *
2711 * @return True on success, false on failure.
[0da4e41]2712 *
2713 */
[01c3bb4]2714bool async_share_out_receive(cap_handle_t *chandle, size_t *size,
2715 unsigned int *flags)
[0da4e41]2716{
[01c3bb4]2717 assert(chandle);
[0da4e41]2718 assert(size);
2719 assert(flags);
[a35b458]2720
[47b7006]2721 ipc_call_t data;
[01c3bb4]2722 *chandle = async_get_call(&data);
[a35b458]2723
[228e490]2724 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
[47b7006]2725 return false;
[a35b458]2726
[0da4e41]2727 *size = (size_t) IPC_GET_ARG2(data);
[47b7006]2728 *flags = (unsigned int) IPC_GET_ARG3(data);
2729 return true;
[0da4e41]2730}
2731
2732/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
2733 *
[47b7006]2734 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
2735 * calls so that the user doesn't have to remember the meaning of each IPC
2736 * argument.
[0da4e41]2737 *
[01c3bb4]2738 * @param chandle Handle of the IPC_M_DATA_WRITE call to answer.
2739 * @param dst Address of the storage for the destination address space area
2740 * base address.
[47b7006]2741 *
[01c3bb4]2742 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]2743 *
2744 */
[b7fd2a0]2745errno_t async_share_out_finalize(cap_handle_t chandle, void **dst)
[0da4e41]2746{
[01c3bb4]2747 return ipc_answer_2(chandle, EOK, (sysarg_t) __entry, (sysarg_t) dst);
[0da4e41]2748}
2749
[8bf1eeb]2750/** Start IPC_M_DATA_READ using the async framework.
2751 *
[79ae36dd]2752 * @param exch Exchange for sending the message.
2753 * @param dst Address of the beginning of the destination buffer.
2754 * @param size Size of the destination buffer (in bytes).
[8bf1eeb]2755 * @param dataptr Storage of call data (arg 2 holds actual data size).
[79ae36dd]2756 *
[8bf1eeb]2757 * @return Hash of the sent message or 0 on error.
[79ae36dd]2758 *
[8bf1eeb]2759 */
[79ae36dd]2760aid_t async_data_read(async_exch_t *exch, void *dst, size_t size,
2761 ipc_call_t *dataptr)
[8bf1eeb]2762{
[79ae36dd]2763 return async_send_2(exch, IPC_M_DATA_READ, (sysarg_t) dst,
[8bf1eeb]2764 (sysarg_t) size, dataptr);
2765}
2766
[47b7006]2767/** Wrapper for IPC_M_DATA_READ calls using the async framework.
[0da4e41]2768 *
[79ae36dd]2769 * @param exch Exchange for sending the message.
2770 * @param dst Address of the beginning of the destination buffer.
2771 * @param size Size of the destination buffer.
[47b7006]2772 *
[cde999a]2773 * @return Zero on success or an error code from errno.h.
[0da4e41]2774 *
2775 */
[b7fd2a0]2776errno_t async_data_read_start(async_exch_t *exch, void *dst, size_t size)
[0da4e41]2777{
[79ae36dd]2778 if (exch == NULL)
2779 return ENOENT;
[a35b458]2780
[79ae36dd]2781 return async_req_2_0(exch, IPC_M_DATA_READ, (sysarg_t) dst,
2782 (sysarg_t) size);
[0da4e41]2783}
2784
2785/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
2786 *
[47b7006]2787 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
2788 * calls so that the user doesn't have to remember the meaning of each IPC
2789 * argument.
[0da4e41]2790 *
2791 * So far, this wrapper is to be used from within a connection fibril.
2792 *
[01c3bb4]2793 * @param chandle Storage for the handle of the IPC_M_DATA_READ.
2794 * @param size Storage for the maximum size. Can be NULL.
[47b7006]2795 *
2796 * @return True on success, false on failure.
[0da4e41]2797 *
2798 */
[01c3bb4]2799bool async_data_read_receive(cap_handle_t *chandle, size_t *size)
[d768d4c8]2800{
2801 ipc_call_t data;
[01c3bb4]2802 return async_data_read_receive_call(chandle, &data, size);
[d768d4c8]2803}
2804
2805/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
2806 *
2807 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
2808 * calls so that the user doesn't have to remember the meaning of each IPC
2809 * argument.
2810 *
2811 * So far, this wrapper is to be used from within a connection fibril.
2812 *
[01c3bb4]2813 * @param chandle Storage for the handle of the IPC_M_DATA_READ.
2814 * @param size Storage for the maximum size. Can be NULL.
[d768d4c8]2815 *
2816 * @return True on success, false on failure.
2817 *
2818 */
[01c3bb4]2819bool async_data_read_receive_call(cap_handle_t *chandle, ipc_call_t *data,
[d768d4c8]2820 size_t *size)
[0da4e41]2821{
[01c3bb4]2822 assert(chandle);
[d768d4c8]2823 assert(data);
[a35b458]2824
[01c3bb4]2825 *chandle = async_get_call(data);
[a35b458]2826
[d768d4c8]2827 if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_READ)
[47b7006]2828 return false;
[a35b458]2829
[0da4e41]2830 if (size)
[d768d4c8]2831 *size = (size_t) IPC_GET_ARG2(*data);
[a35b458]2832
[47b7006]2833 return true;
[0da4e41]2834}
2835
2836/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
2837 *
[47b7006]2838 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
2839 * calls so that the user doesn't have to remember the meaning of each IPC
2840 * argument.
[0da4e41]2841 *
[01c3bb4]2842 * @param chandle Handle of the IPC_M_DATA_READ call to answer.
2843 * @param src Source address for the IPC_M_DATA_READ call.
2844 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
2845 * the maximum size announced by the sender.
[47b7006]2846 *
[01c3bb4]2847 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]2848 *
2849 */
[b7fd2a0]2850errno_t async_data_read_finalize(cap_handle_t chandle, const void *src, size_t size)
[0da4e41]2851{
[01c3bb4]2852 return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) size);
[0da4e41]2853}
2854
[b4cbef1]2855/** Wrapper for forwarding any read request
2856 *
2857 */
[b7fd2a0]2858errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
[79ae36dd]2859 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
2860 ipc_call_t *dataptr)
[b4cbef1]2861{
[79ae36dd]2862 if (exch == NULL)
2863 return ENOENT;
[a35b458]2864
[01c3bb4]2865 cap_handle_t chandle;
2866 if (!async_data_read_receive(&chandle, NULL)) {
2867 ipc_answer_0(chandle, EINVAL);
[b4cbef1]2868 return EINVAL;
2869 }
[a35b458]2870
[79ae36dd]2871 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]2872 dataptr);
2873 if (msg == 0) {
[01c3bb4]2874 ipc_answer_0(chandle, EINVAL);
[b4cbef1]2875 return EINVAL;
2876 }
[a35b458]2877
[b7fd2a0]2878 errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
[b4cbef1]2879 IPC_FF_ROUTE_FROM_ME);
2880 if (retval != EOK) {
[ab9f443]2881 async_forget(msg);
[01c3bb4]2882 ipc_answer_0(chandle, retval);
[b4cbef1]2883 return retval;
2884 }
[a35b458]2885
[b7fd2a0]2886 errno_t rc;
[b4cbef1]2887 async_wait_for(msg, &rc);
[a35b458]2888
[b7fd2a0]2889 return (errno_t) rc;
[b4cbef1]2890}
2891
[47b7006]2892/** Wrapper for IPC_M_DATA_WRITE calls using the async framework.
[0da4e41]2893 *
[79ae36dd]2894 * @param exch Exchange for sending the message.
2895 * @param src Address of the beginning of the source buffer.
2896 * @param size Size of the source buffer.
[b4cbef1]2897 *
[cde999a]2898 * @return Zero on success or an error code from errno.h.
[0da4e41]2899 *
2900 */
[b7fd2a0]2901errno_t async_data_write_start(async_exch_t *exch, const void *src, size_t size)
[0da4e41]2902{
[79ae36dd]2903 if (exch == NULL)
2904 return ENOENT;
[a35b458]2905
[79ae36dd]2906 return async_req_2_0(exch, IPC_M_DATA_WRITE, (sysarg_t) src,
2907 (sysarg_t) size);
[0da4e41]2908}
2909
2910/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
2911 *
[47b7006]2912 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
2913 * calls so that the user doesn't have to remember the meaning of each IPC
2914 * argument.
[0da4e41]2915 *
2916 * So far, this wrapper is to be used from within a connection fibril.
2917 *
[01c3bb4]2918 * @param chandle Storage for the handle of the IPC_M_DATA_WRITE.
2919 * @param size Storage for the suggested size. May be NULL.
[b4cbef1]2920 *
[01c3bb4]2921 * @return True on success, false on failure.
[0da4e41]2922 *
2923 */
[01c3bb4]2924bool async_data_write_receive(cap_handle_t *chandle, size_t *size)
[5ae1c51]2925{
2926 ipc_call_t data;
[01c3bb4]2927 return async_data_write_receive_call(chandle, &data, size);
[5ae1c51]2928}
2929
2930/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
2931 *
2932 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
2933 * calls so that the user doesn't have to remember the meaning of each IPC
2934 * argument.
2935 *
2936 * So far, this wrapper is to be used from within a connection fibril.
2937 *
[01c3bb4]2938 * @param chandle Storage for the handle of the IPC_M_DATA_WRITE.
2939 * @param data Storage for the ipc call data.
2940 * @param size Storage for the suggested size. May be NULL.
[5ae1c51]2941 *
2942 * @return True on success, false on failure.
2943 *
2944 */
[01c3bb4]2945bool async_data_write_receive_call(cap_handle_t *chandle, ipc_call_t *data,
[5ae1c51]2946 size_t *size)
[0da4e41]2947{
[01c3bb4]2948 assert(chandle);
[5ae1c51]2949 assert(data);
[a35b458]2950
[01c3bb4]2951 *chandle = async_get_call(data);
[a35b458]2952
[5ae1c51]2953 if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_WRITE)
[47b7006]2954 return false;
[a35b458]2955
[0da4e41]2956 if (size)
[5ae1c51]2957 *size = (size_t) IPC_GET_ARG2(*data);
[a35b458]2958
[47b7006]2959 return true;
[0da4e41]2960}
2961
2962/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
2963 *
[47b7006]2964 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
2965 * calls so that the user doesn't have to remember the meaning of each IPC
2966 * argument.
[0da4e41]2967 *
[01c3bb4]2968 * @param chandle Handle of the IPC_M_DATA_WRITE call to answer.
2969 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
2970 * @param size Final size for the IPC_M_DATA_WRITE call.
[b4cbef1]2971 *
[01c3bb4]2972 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]2973 *
2974 */
[b7fd2a0]2975errno_t async_data_write_finalize(cap_handle_t chandle, void *dst, size_t size)
[0da4e41]2976{
[01c3bb4]2977 return ipc_answer_2(chandle, EOK, (sysarg_t) dst, (sysarg_t) size);
[0da4e41]2978}
2979
[eda925a]2980/** Wrapper for receiving binary data or strings
[8aa42e3]2981 *
2982 * This wrapper only makes it more comfortable to use async_data_write_*
[eda925a]2983 * functions to receive binary data or strings.
[8aa42e3]2984 *
[472c09d]2985 * @param data Pointer to data pointer (which should be later disposed
2986 * by free()). If the operation fails, the pointer is not
2987 * touched.
[eda925a]2988 * @param nullterm If true then the received data is always zero terminated.
2989 * This also causes to allocate one extra byte beyond the
2990 * raw transmitted data.
[b4cbef1]2991 * @param min_size Minimum size (in bytes) of the data to receive.
[472c09d]2992 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
2993 * no limit.
[eda925a]2994 * @param granulariy If non-zero then the size of the received data has to
[472c09d]2995 * be divisible by this value.
2996 * @param received If not NULL, the size of the received data is stored here.
[8aa42e3]2997 *
2998 * @return Zero on success or a value from @ref errno.h on failure.
2999 *
3000 */
[b7fd2a0]3001errno_t async_data_write_accept(void **data, const bool nullterm,
[eda925a]3002 const size_t min_size, const size_t max_size, const size_t granularity,
3003 size_t *received)
[8aa42e3]3004{
[79ae36dd]3005 assert(data);
[a35b458]3006
[01c3bb4]3007 cap_handle_t chandle;
[8aa42e3]3008 size_t size;
[01c3bb4]3009 if (!async_data_write_receive(&chandle, &size)) {
3010 ipc_answer_0(chandle, EINVAL);
[8aa42e3]3011 return EINVAL;
3012 }
[a35b458]3013
[b4cbef1]3014 if (size < min_size) {
[01c3bb4]3015 ipc_answer_0(chandle, EINVAL);
[b4cbef1]3016 return EINVAL;
3017 }
[a35b458]3018
[8aa42e3]3019 if ((max_size > 0) && (size > max_size)) {
[01c3bb4]3020 ipc_answer_0(chandle, EINVAL);
[8aa42e3]3021 return EINVAL;
3022 }
[a35b458]3023
[472c09d]3024 if ((granularity > 0) && ((size % granularity) != 0)) {
[01c3bb4]3025 ipc_answer_0(chandle, EINVAL);
[472c09d]3026 return EINVAL;
3027 }
[a35b458]3028
[57dea62]3029 void *arg_data;
[a35b458]3030
[eda925a]3031 if (nullterm)
[57dea62]3032 arg_data = malloc(size + 1);
[eda925a]3033 else
[57dea62]3034 arg_data = malloc(size);
[a35b458]3035
[57dea62]3036 if (arg_data == NULL) {
[01c3bb4]3037 ipc_answer_0(chandle, ENOMEM);
[8aa42e3]3038 return ENOMEM;
3039 }
[a35b458]3040
[b7fd2a0]3041 errno_t rc = async_data_write_finalize(chandle, arg_data, size);
[8aa42e3]3042 if (rc != EOK) {
[57dea62]3043 free(arg_data);
[8aa42e3]3044 return rc;
3045 }
[a35b458]3046
[eda925a]3047 if (nullterm)
[57dea62]3048 ((char *) arg_data)[size] = 0;
[a35b458]3049
[57dea62]3050 *data = arg_data;
[472c09d]3051 if (received != NULL)
3052 *received = size;
[a35b458]3053
[8aa42e3]3054 return EOK;
3055}
3056
[b4cbef1]3057/** Wrapper for voiding any data that is about to be received
3058 *
3059 * This wrapper can be used to void any pending data
3060 *
3061 * @param retval Error value from @ref errno.h to be returned to the caller.
3062 *
3063 */
[b7fd2a0]3064void async_data_write_void(errno_t retval)
[b4cbef1]3065{
[01c3bb4]3066 cap_handle_t chandle;
3067 async_data_write_receive(&chandle, NULL);
3068 ipc_answer_0(chandle, retval);
[b4cbef1]3069}
3070
3071/** Wrapper for forwarding any data that is about to be received
3072 *
3073 */
[b7fd2a0]3074errno_t async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
[79ae36dd]3075 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
3076 ipc_call_t *dataptr)
[b4cbef1]3077{
[79ae36dd]3078 if (exch == NULL)
3079 return ENOENT;
[a35b458]3080
[01c3bb4]3081 cap_handle_t chandle;
3082 if (!async_data_write_receive(&chandle, NULL)) {
3083 ipc_answer_0(chandle, EINVAL);
[b4cbef1]3084 return EINVAL;
3085 }
[a35b458]3086
[79ae36dd]3087 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]3088 dataptr);
3089 if (msg == 0) {
[01c3bb4]3090 ipc_answer_0(chandle, EINVAL);
[b4cbef1]3091 return EINVAL;
3092 }
[a35b458]3093
[b7fd2a0]3094 errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
[b4cbef1]3095 IPC_FF_ROUTE_FROM_ME);
3096 if (retval != EOK) {
[ab9f443]3097 async_forget(msg);
[01c3bb4]3098 ipc_answer_0(chandle, retval);
[b4cbef1]3099 return retval;
3100 }
[a35b458]3101
[b7fd2a0]3102 errno_t rc;
[b4cbef1]3103 async_wait_for(msg, &rc);
[a35b458]3104
[b7fd2a0]3105 return (errno_t) rc;
[b4cbef1]3106}
3107
[79ae36dd]3108/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
3109 *
3110 * If the current call is IPC_M_CONNECT_TO_ME then a new
3111 * async session is created for the accepted phone.
3112 *
3113 * @param mgmt Exchange management style.
3114 *
[8869f7b]3115 * @return New async session.
3116 * @return NULL on failure.
[79ae36dd]3117 *
3118 */
3119async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
3120{
3121 /* Accept the phone */
3122 ipc_call_t call;
[01c3bb4]3123 cap_handle_t chandle = async_get_call(&call);
3124 cap_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(call);
[a35b458]3125
[01c3bb4]3126 if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) || (phandle < 0)) {
3127 async_answer_0(chandle, EINVAL);
[79ae36dd]3128 return NULL;
3129 }
[a35b458]3130
[79ae36dd]3131 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
3132 if (sess == NULL) {
[01c3bb4]3133 async_answer_0(chandle, ENOMEM);
[79ae36dd]3134 return NULL;
3135 }
[a35b458]3136
[566992e1]3137 sess->iface = 0;
[79ae36dd]3138 sess->mgmt = mgmt;
[01c3bb4]3139 sess->phone = phandle;
[79ae36dd]3140 sess->arg1 = 0;
3141 sess->arg2 = 0;
3142 sess->arg3 = 0;
[a35b458]3143
[58cbf8d5]3144 fibril_mutex_initialize(&sess->remote_state_mtx);
3145 sess->remote_state_data = NULL;
[a35b458]3146
[79ae36dd]3147 list_initialize(&sess->exch_list);
3148 fibril_mutex_initialize(&sess->mutex);
3149 atomic_set(&sess->refcnt, 0);
[a35b458]3150
[79ae36dd]3151 /* Acknowledge the connected phone */
[01c3bb4]3152 async_answer_0(chandle, EOK);
[a35b458]3153
[79ae36dd]3154 return sess;
3155}
3156
[8869f7b]3157/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
3158 *
3159 * If the call is IPC_M_CONNECT_TO_ME then a new
3160 * async session is created. However, the phone is
3161 * not accepted automatically.
3162 *
3163 * @param mgmt Exchange management style.
3164 * @param call Call data.
3165 *
3166 * @return New async session.
3167 * @return NULL on failure.
3168 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
3169 *
3170 */
3171async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
3172{
[01c3bb4]3173 cap_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(*call);
[a35b458]3174
[01c3bb4]3175 if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) || (phandle < 0))
[8869f7b]3176 return NULL;
[a35b458]3177
[8869f7b]3178 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
3179 if (sess == NULL)
3180 return NULL;
[a35b458]3181
[566992e1]3182 sess->iface = 0;
[8869f7b]3183 sess->mgmt = mgmt;
[01c3bb4]3184 sess->phone = phandle;
[8869f7b]3185 sess->arg1 = 0;
3186 sess->arg2 = 0;
3187 sess->arg3 = 0;
[a35b458]3188
[58cbf8d5]3189 fibril_mutex_initialize(&sess->remote_state_mtx);
3190 sess->remote_state_data = NULL;
[a35b458]3191
[8869f7b]3192 list_initialize(&sess->exch_list);
3193 fibril_mutex_initialize(&sess->mutex);
3194 atomic_set(&sess->refcnt, 0);
[a35b458]3195
[8869f7b]3196 return sess;
3197}
3198
[b7fd2a0]3199errno_t async_state_change_start(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
[2c4aa39]3200 sysarg_t arg3, async_exch_t *other_exch)
3201{
3202 return async_req_5_0(exch, IPC_M_STATE_CHANGE_AUTHORIZE,
3203 arg1, arg2, arg3, 0, other_exch->phone);
3204}
3205
[01c3bb4]3206bool async_state_change_receive(cap_handle_t *chandle, sysarg_t *arg1,
[2c4aa39]3207 sysarg_t *arg2, sysarg_t *arg3)
3208{
[01c3bb4]3209 assert(chandle);
[a35b458]3210
[2c4aa39]3211 ipc_call_t call;
[01c3bb4]3212 *chandle = async_get_call(&call);
[a35b458]3213
[2c4aa39]3214 if (IPC_GET_IMETHOD(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
3215 return false;
[a35b458]3216
[2c4aa39]3217 if (arg1)
3218 *arg1 = IPC_GET_ARG1(call);
3219 if (arg2)
3220 *arg2 = IPC_GET_ARG2(call);
3221 if (arg3)
3222 *arg3 = IPC_GET_ARG3(call);
[a35b458]3223
[2c4aa39]3224 return true;
3225}
3226
[b7fd2a0]3227errno_t async_state_change_finalize(cap_handle_t chandle, async_exch_t *other_exch)
[2c4aa39]3228{
[01c3bb4]3229 return ipc_answer_1(chandle, EOK, other_exch->phone);
[2c4aa39]3230}
3231
[58cbf8d5]3232/** Lock and get session remote state
3233 *
3234 * Lock and get the local replica of the remote state
3235 * in stateful sessions. The call should be paired
3236 * with async_remote_state_release*().
3237 *
3238 * @param[in] sess Stateful session.
3239 *
3240 * @return Local replica of the remote state.
3241 *
3242 */
3243void *async_remote_state_acquire(async_sess_t *sess)
3244{
3245 fibril_mutex_lock(&sess->remote_state_mtx);
3246 return sess->remote_state_data;
3247}
3248
3249/** Update the session remote state
3250 *
3251 * Update the local replica of the remote state
3252 * in stateful sessions. The remote state must
3253 * be already locked.
3254 *
3255 * @param[in] sess Stateful session.
3256 * @param[in] state New local replica of the remote state.
3257 *
3258 */
3259void async_remote_state_update(async_sess_t *sess, void *state)
3260{
3261 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
3262 sess->remote_state_data = state;
3263}
3264
3265/** Release the session remote state
3266 *
3267 * Unlock the local replica of the remote state
3268 * in stateful sessions.
3269 *
3270 * @param[in] sess Stateful session.
3271 *
3272 */
3273void async_remote_state_release(async_sess_t *sess)
3274{
3275 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
[a35b458]3276
[58cbf8d5]3277 fibril_mutex_unlock(&sess->remote_state_mtx);
3278}
3279
3280/** Release the session remote state and end an exchange
3281 *
3282 * Unlock the local replica of the remote state
3283 * in stateful sessions. This is convenience function
3284 * which gets the session pointer from the exchange
3285 * and also ends the exchange.
3286 *
3287 * @param[in] exch Stateful session's exchange.
3288 *
3289 */
3290void async_remote_state_release_exchange(async_exch_t *exch)
3291{
3292 if (exch == NULL)
3293 return;
[a35b458]3294
[58cbf8d5]3295 async_sess_t *sess = exch->sess;
3296 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
[a35b458]3297
[58cbf8d5]3298 async_exchange_end(exch);
3299 fibril_mutex_unlock(&sess->remote_state_mtx);
3300}
3301
[101516d]3302void *async_as_area_create(void *base, size_t size, unsigned int flags,
[ae6021d]3303 async_sess_t *pager, sysarg_t id1, sysarg_t id2, sysarg_t id3)
3304{
3305 as_area_pager_info_t pager_info = {
3306 .pager = pager->phone,
3307 .id1 = id1,
3308 .id2 = id2,
3309 .id3 = id3
3310 };
3311 return as_area_create(base, size, flags, &pager_info);
[101516d]3312}
3313
[a46da63]3314/** @}
[b2951e2]3315 */
Note: See TracBrowser for help on using the repository browser.