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

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

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

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

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

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

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