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

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

Do not create a new fibril for each IRQ notification

In the absence of fibril serialization, manager fibrils can
theoretically block in async IPC or on fibril synchronization
primitives. Consequently, it is safe to execute the IRQ handler directly
from the manager fibril. The manager fibril can block while processing
the notification, but most of the times it will not block and the
handler will execute atomically.

This changeset modifies the current behaviour so that we no longer spawn
a new notification fibril for each IRQ, but rather execute the handler
directly from the manager fibril and test if the execution blocked. If
it blocked, the manager fibril had assumed the role of a notification
fibril and we destroy it afterwards - merely to avoid fibril population
explosion. Otherwise, which is the usual behavior, we keep it so that
it resumes its job of a manager fibril.

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