source: mainline/uspace/lib/c/generic/async.c@ 8610c2c

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

IPC return values are always errno constants. Adjust types to reflect that.

In principle, IPC server is not allowed to return non-errno values via
the "main" return value, because kernel interprets it (e.g. EHANGUP).
It's still possible to return arbitrary additional return values alongside EOK,
which are not interpreted in normal communication.

  • Property mode set to 100644
File size: 79.8 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 int 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
513int 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 int 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 0;
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 a negative error code.
847 *
848 */
849int 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 int ret;
863 async_wait_for(req, &ret);
864 if (ret != EOK)
865 return (int) 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 Negative error code.
1056 *
1057 */
1058int 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 int 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 a negative error code.
1092 *
1093 */
1094int 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 a negative error code.
1109 *
1110 */
1111int 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 a negative error code.
1142 *
1143 */
1144int 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 */
1175int 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 */
1187int 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 int 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 int 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 == 0) {
1499 /* This neither a notification nor an answer. */
1500 handle_expired_timeouts();
1501 continue;
1502 }
1503 }
1504
1505 if (call.flags & IPC_CALL_ANSWERED)
1506 continue;
1507
1508 handle_call(call.cap_handle, &call);
1509 }
1510
1511 return 0;
1512}
1513
1514/** Function to start async_manager as a standalone fibril.
1515 *
1516 * When more kernel threads are used, one async manager should exist per thread.
1517 *
1518 * @param arg Unused.
1519 * @return Never returns.
1520 *
1521 */
1522static int async_manager_fibril(void *arg)
1523{
1524 futex_up(&async_futex);
1525
1526 /*
1527 * async_futex is always locked when entering manager
1528 */
1529 async_manager_worker();
1530
1531 return 0;
1532}
1533
1534/** Add one manager to manager list. */
1535void async_create_manager(void)
1536{
1537 fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
1538 if (fid != 0)
1539 fibril_add_manager(fid);
1540}
1541
1542/** Remove one manager from manager list */
1543void async_destroy_manager(void)
1544{
1545 fibril_remove_manager();
1546}
1547
1548/** Initialize the async framework.
1549 *
1550 */
1551void __async_init(void)
1552{
1553 if (!hash_table_create(&interface_hash_table, 0, 0,
1554 &interface_hash_table_ops))
1555 abort();
1556
1557 if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
1558 abort();
1559
1560 if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
1561 abort();
1562
1563 if (!hash_table_create(&notification_hash_table, 0, 0,
1564 &notification_hash_table_ops))
1565 abort();
1566
1567 session_ns = (async_sess_t *) malloc(sizeof(async_sess_t));
1568 if (session_ns == NULL)
1569 abort();
1570
1571 session_ns->iface = 0;
1572 session_ns->mgmt = EXCHANGE_ATOMIC;
1573 session_ns->phone = PHONE_NS;
1574 session_ns->arg1 = 0;
1575 session_ns->arg2 = 0;
1576 session_ns->arg3 = 0;
1577
1578 fibril_mutex_initialize(&session_ns->remote_state_mtx);
1579 session_ns->remote_state_data = NULL;
1580
1581 list_initialize(&session_ns->exch_list);
1582 fibril_mutex_initialize(&session_ns->mutex);
1583 atomic_set(&session_ns->refcnt, 0);
1584}
1585
1586/** Reply received callback.
1587 *
1588 * This function is called whenever a reply for an asynchronous message sent out
1589 * by the asynchronous framework is received.
1590 *
1591 * Notify the fibril which is waiting for this message that it has arrived.
1592 *
1593 * @param arg Pointer to the asynchronous message record.
1594 * @param retval Value returned in the answer.
1595 * @param data Call data of the answer.
1596 *
1597 */
1598void reply_received(void *arg, int retval, ipc_call_t *data)
1599{
1600 assert(arg);
1601
1602 futex_down(&async_futex);
1603
1604 amsg_t *msg = (amsg_t *) arg;
1605 msg->retval = retval;
1606
1607 /* Copy data after futex_down, just in case the call was detached */
1608 if ((msg->dataptr) && (data))
1609 *msg->dataptr = *data;
1610
1611 write_barrier();
1612
1613 /* Remove message from timeout list */
1614 if (msg->wdata.to_event.inlist)
1615 list_remove(&msg->wdata.to_event.link);
1616
1617 msg->done = true;
1618
1619 if (msg->forget) {
1620 assert(msg->wdata.active);
1621 amsg_destroy(msg);
1622 } else if (!msg->wdata.active) {
1623 msg->wdata.active = true;
1624 fibril_add_ready(msg->wdata.fid);
1625 }
1626
1627 futex_up(&async_futex);
1628}
1629
1630/** Send message and return id of the sent message.
1631 *
1632 * The return value can be used as input for async_wait() to wait for
1633 * completion.
1634 *
1635 * @param exch Exchange for sending the message.
1636 * @param imethod Service-defined interface and method.
1637 * @param arg1 Service-defined payload argument.
1638 * @param arg2 Service-defined payload argument.
1639 * @param arg3 Service-defined payload argument.
1640 * @param arg4 Service-defined payload argument.
1641 * @param dataptr If non-NULL, storage where the reply data will be stored.
1642 *
1643 * @return Hash of the sent message or 0 on error.
1644 *
1645 */
1646aid_t async_send_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1647 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
1648{
1649 if (exch == NULL)
1650 return 0;
1651
1652 amsg_t *msg = amsg_create();
1653 if (msg == NULL)
1654 return 0;
1655
1656 msg->dataptr = dataptr;
1657 msg->wdata.active = true;
1658
1659 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4, msg,
1660 reply_received);
1661
1662 return (aid_t) msg;
1663}
1664
1665/** Send message and return id of the sent message
1666 *
1667 * The return value can be used as input for async_wait() to wait for
1668 * completion.
1669 *
1670 * @param exch Exchange for sending the message.
1671 * @param imethod Service-defined interface and method.
1672 * @param arg1 Service-defined payload argument.
1673 * @param arg2 Service-defined payload argument.
1674 * @param arg3 Service-defined payload argument.
1675 * @param arg4 Service-defined payload argument.
1676 * @param arg5 Service-defined payload argument.
1677 * @param dataptr If non-NULL, storage where the reply data will be
1678 * stored.
1679 *
1680 * @return Hash of the sent message or 0 on error.
1681 *
1682 */
1683aid_t async_send_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1684 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
1685 ipc_call_t *dataptr)
1686{
1687 if (exch == NULL)
1688 return 0;
1689
1690 amsg_t *msg = amsg_create();
1691 if (msg == NULL)
1692 return 0;
1693
1694 msg->dataptr = dataptr;
1695 msg->wdata.active = true;
1696
1697 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4, arg5,
1698 msg, reply_received);
1699
1700 return (aid_t) msg;
1701}
1702
1703/** Wait for a message sent by the async framework.
1704 *
1705 * @param amsgid Hash of the message to wait for.
1706 * @param retval Pointer to storage where the retval of the answer will
1707 * be stored.
1708 *
1709 */
1710void async_wait_for(aid_t amsgid, int *retval)
1711{
1712 assert(amsgid);
1713
1714 amsg_t *msg = (amsg_t *) amsgid;
1715
1716 futex_down(&async_futex);
1717
1718 assert(!msg->forget);
1719 assert(!msg->destroyed);
1720
1721 if (msg->done) {
1722 futex_up(&async_futex);
1723 goto done;
1724 }
1725
1726 msg->wdata.fid = fibril_get_id();
1727 msg->wdata.active = false;
1728 msg->wdata.to_event.inlist = false;
1729
1730 /* Leave the async_futex locked when entering this function */
1731 fibril_switch(FIBRIL_TO_MANAGER);
1732
1733 /* Futex is up automatically after fibril_switch */
1734
1735done:
1736 if (retval)
1737 *retval = msg->retval;
1738
1739 amsg_destroy(msg);
1740}
1741
1742/** Wait for a message sent by the async framework, timeout variant.
1743 *
1744 * If the wait times out, the caller may choose to either wait again by calling
1745 * async_wait_for() or async_wait_timeout(), or forget the message via
1746 * async_forget().
1747 *
1748 * @param amsgid Hash of the message to wait for.
1749 * @param retval Pointer to storage where the retval of the answer will
1750 * be stored.
1751 * @param timeout Timeout in microseconds.
1752 *
1753 * @return Zero on success, ETIMEOUT if the timeout has expired.
1754 *
1755 */
1756int async_wait_timeout(aid_t amsgid, int *retval, suseconds_t timeout)
1757{
1758 assert(amsgid);
1759
1760 amsg_t *msg = (amsg_t *) amsgid;
1761
1762 futex_down(&async_futex);
1763
1764 assert(!msg->forget);
1765 assert(!msg->destroyed);
1766
1767 if (msg->done) {
1768 futex_up(&async_futex);
1769 goto done;
1770 }
1771
1772 /*
1773 * Negative timeout is converted to zero timeout to avoid
1774 * using tv_add with negative augmenter.
1775 */
1776 if (timeout < 0)
1777 timeout = 0;
1778
1779 getuptime(&msg->wdata.to_event.expires);
1780 tv_add_diff(&msg->wdata.to_event.expires, timeout);
1781
1782 /*
1783 * Current fibril is inserted as waiting regardless of the
1784 * "size" of the timeout.
1785 *
1786 * Checking for msg->done and immediately bailing out when
1787 * timeout == 0 would mean that the manager fibril would never
1788 * run (consider single threaded program).
1789 * Thus the IPC answer would be never retrieved from the kernel.
1790 *
1791 * Notice that the actual delay would be very small because we
1792 * - switch to manager fibril
1793 * - the manager sees expired timeout
1794 * - and thus adds us back to ready queue
1795 * - manager switches back to some ready fibril
1796 * (prior it, it checks for incoming IPC).
1797 *
1798 */
1799 msg->wdata.fid = fibril_get_id();
1800 msg->wdata.active = false;
1801 async_insert_timeout(&msg->wdata);
1802
1803 /* Leave the async_futex locked when entering this function */
1804 fibril_switch(FIBRIL_TO_MANAGER);
1805
1806 /* Futex is up automatically after fibril_switch */
1807
1808 if (!msg->done)
1809 return ETIMEOUT;
1810
1811done:
1812 if (retval)
1813 *retval = msg->retval;
1814
1815 amsg_destroy(msg);
1816
1817 return 0;
1818}
1819
1820/** Discard the message / reply on arrival.
1821 *
1822 * The message will be marked to be discarded once the reply arrives in
1823 * reply_received(). It is not allowed to call async_wait_for() or
1824 * async_wait_timeout() on this message after a call to this function.
1825 *
1826 * @param amsgid Hash of the message to forget.
1827 */
1828void async_forget(aid_t amsgid)
1829{
1830 amsg_t *msg = (amsg_t *) amsgid;
1831
1832 assert(msg);
1833 assert(!msg->forget);
1834 assert(!msg->destroyed);
1835
1836 futex_down(&async_futex);
1837
1838 if (msg->done) {
1839 amsg_destroy(msg);
1840 } else {
1841 msg->dataptr = NULL;
1842 msg->forget = true;
1843 }
1844
1845 futex_up(&async_futex);
1846}
1847
1848/** Wait for specified time.
1849 *
1850 * The current fibril is suspended but the thread continues to execute.
1851 *
1852 * @param timeout Duration of the wait in microseconds.
1853 *
1854 */
1855void async_usleep(suseconds_t timeout)
1856{
1857 amsg_t *msg = amsg_create();
1858 if (!msg)
1859 return;
1860
1861 msg->wdata.fid = fibril_get_id();
1862
1863 getuptime(&msg->wdata.to_event.expires);
1864 tv_add_diff(&msg->wdata.to_event.expires, timeout);
1865
1866 futex_down(&async_futex);
1867
1868 async_insert_timeout(&msg->wdata);
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 amsg_destroy(msg);
1876}
1877
1878/** Delay execution for the specified number of seconds
1879 *
1880 * @param sec Number of seconds to sleep
1881 */
1882void async_sleep(unsigned int sec)
1883{
1884 /*
1885 * Sleep in 1000 second steps to support
1886 * full argument range
1887 */
1888
1889 while (sec > 0) {
1890 unsigned int period = (sec > 1000) ? 1000 : sec;
1891
1892 async_usleep(period * 1000000);
1893 sec -= period;
1894 }
1895}
1896
1897/** Pseudo-synchronous message sending - fast version.
1898 *
1899 * Send message asynchronously and return only after the reply arrives.
1900 *
1901 * This function can only transfer 4 register payload arguments. For
1902 * transferring more arguments, see the slower async_req_slow().
1903 *
1904 * @param exch Exchange for sending the message.
1905 * @param imethod Interface and method of the call.
1906 * @param arg1 Service-defined payload argument.
1907 * @param arg2 Service-defined payload argument.
1908 * @param arg3 Service-defined payload argument.
1909 * @param arg4 Service-defined payload argument.
1910 * @param r1 If non-NULL, storage for the 1st reply argument.
1911 * @param r2 If non-NULL, storage for the 2nd reply argument.
1912 * @param r3 If non-NULL, storage for the 3rd reply argument.
1913 * @param r4 If non-NULL, storage for the 4th reply argument.
1914 * @param r5 If non-NULL, storage for the 5th reply argument.
1915 *
1916 * @return Return code of the reply or a negative error code.
1917 *
1918 */
1919int async_req_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1920 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
1921 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
1922{
1923 if (exch == NULL)
1924 return ENOENT;
1925
1926 ipc_call_t result;
1927 aid_t aid = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
1928 &result);
1929
1930 int rc;
1931 async_wait_for(aid, &rc);
1932
1933 if (r1)
1934 *r1 = IPC_GET_ARG1(result);
1935
1936 if (r2)
1937 *r2 = IPC_GET_ARG2(result);
1938
1939 if (r3)
1940 *r3 = IPC_GET_ARG3(result);
1941
1942 if (r4)
1943 *r4 = IPC_GET_ARG4(result);
1944
1945 if (r5)
1946 *r5 = IPC_GET_ARG5(result);
1947
1948 return rc;
1949}
1950
1951/** Pseudo-synchronous message sending - slow version.
1952 *
1953 * Send message asynchronously and return only after the reply arrives.
1954 *
1955 * @param exch Exchange for sending the message.
1956 * @param imethod Interface and method of the call.
1957 * @param arg1 Service-defined payload argument.
1958 * @param arg2 Service-defined payload argument.
1959 * @param arg3 Service-defined payload argument.
1960 * @param arg4 Service-defined payload argument.
1961 * @param arg5 Service-defined payload argument.
1962 * @param r1 If non-NULL, storage for the 1st reply argument.
1963 * @param r2 If non-NULL, storage for the 2nd reply argument.
1964 * @param r3 If non-NULL, storage for the 3rd reply argument.
1965 * @param r4 If non-NULL, storage for the 4th reply argument.
1966 * @param r5 If non-NULL, storage for the 5th reply argument.
1967 *
1968 * @return Return code of the reply or a negative error code.
1969 *
1970 */
1971int async_req_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
1972 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
1973 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
1974{
1975 if (exch == NULL)
1976 return ENOENT;
1977
1978 ipc_call_t result;
1979 aid_t aid = async_send_5(exch, imethod, arg1, arg2, arg3, arg4, arg5,
1980 &result);
1981
1982 int rc;
1983 async_wait_for(aid, &rc);
1984
1985 if (r1)
1986 *r1 = IPC_GET_ARG1(result);
1987
1988 if (r2)
1989 *r2 = IPC_GET_ARG2(result);
1990
1991 if (r3)
1992 *r3 = IPC_GET_ARG3(result);
1993
1994 if (r4)
1995 *r4 = IPC_GET_ARG4(result);
1996
1997 if (r5)
1998 *r5 = IPC_GET_ARG5(result);
1999
2000 return rc;
2001}
2002
2003void async_msg_0(async_exch_t *exch, sysarg_t imethod)
2004{
2005 if (exch != NULL)
2006 ipc_call_async_0(exch->phone, imethod, NULL, NULL);
2007}
2008
2009void async_msg_1(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1)
2010{
2011 if (exch != NULL)
2012 ipc_call_async_1(exch->phone, imethod, arg1, NULL, NULL);
2013}
2014
2015void async_msg_2(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2016 sysarg_t arg2)
2017{
2018 if (exch != NULL)
2019 ipc_call_async_2(exch->phone, imethod, arg1, arg2, NULL, NULL);
2020}
2021
2022void async_msg_3(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2023 sysarg_t arg2, sysarg_t arg3)
2024{
2025 if (exch != NULL)
2026 ipc_call_async_3(exch->phone, imethod, arg1, arg2, arg3, NULL,
2027 NULL);
2028}
2029
2030void async_msg_4(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2031 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
2032{
2033 if (exch != NULL)
2034 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4,
2035 NULL, NULL);
2036}
2037
2038void async_msg_5(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
2039 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
2040{
2041 if (exch != NULL)
2042 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4,
2043 arg5, NULL, NULL);
2044}
2045
2046int async_answer_0(cap_handle_t chandle, int retval)
2047{
2048 return ipc_answer_0(chandle, retval);
2049}
2050
2051int async_answer_1(cap_handle_t chandle, int retval, sysarg_t arg1)
2052{
2053 return ipc_answer_1(chandle, retval, arg1);
2054}
2055
2056int async_answer_2(cap_handle_t chandle, int retval, sysarg_t arg1,
2057 sysarg_t arg2)
2058{
2059 return ipc_answer_2(chandle, retval, arg1, arg2);
2060}
2061
2062int async_answer_3(cap_handle_t chandle, int retval, sysarg_t arg1,
2063 sysarg_t arg2, sysarg_t arg3)
2064{
2065 return ipc_answer_3(chandle, retval, arg1, arg2, arg3);
2066}
2067
2068int async_answer_4(cap_handle_t chandle, int retval, sysarg_t arg1,
2069 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
2070{
2071 return ipc_answer_4(chandle, retval, arg1, arg2, arg3, arg4);
2072}
2073
2074int async_answer_5(cap_handle_t chandle, int retval, sysarg_t arg1,
2075 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
2076{
2077 return ipc_answer_5(chandle, retval, arg1, arg2, arg3, arg4, arg5);
2078}
2079
2080int async_forward_fast(cap_handle_t chandle, async_exch_t *exch,
2081 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
2082{
2083 if (exch == NULL)
2084 return ENOENT;
2085
2086 return ipc_forward_fast(chandle, exch->phone, imethod, arg1, arg2, mode);
2087}
2088
2089int async_forward_slow(cap_handle_t chandle, async_exch_t *exch,
2090 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
2091 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
2092{
2093 if (exch == NULL)
2094 return ENOENT;
2095
2096 return ipc_forward_slow(chandle, exch->phone, imethod, arg1, arg2, arg3,
2097 arg4, arg5, mode);
2098}
2099
2100/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
2101 *
2102 * Ask through phone for a new connection to some service.
2103 *
2104 * @param exch Exchange for sending the message.
2105 * @param arg1 User defined argument.
2106 * @param arg2 User defined argument.
2107 * @param arg3 User defined argument.
2108 *
2109 * @return Zero on success or a negative error code.
2110 *
2111 */
2112int async_connect_to_me(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
2113 sysarg_t arg3)
2114{
2115 if (exch == NULL)
2116 return ENOENT;
2117
2118 ipc_call_t answer;
2119 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
2120 &answer);
2121
2122 int rc;
2123 async_wait_for(req, &rc);
2124 if (rc != EOK)
2125 return (int) rc;
2126
2127 return EOK;
2128}
2129
2130static int async_connect_me_to_internal(int phone, sysarg_t arg1, sysarg_t arg2,
2131 sysarg_t arg3, sysarg_t arg4, int *out_phone)
2132{
2133 ipc_call_t result;
2134
2135 // XXX: Workaround for GCC's inability to infer association between
2136 // rc == EOK and *out_phone being assigned.
2137 *out_phone = -1;
2138
2139 amsg_t *msg = amsg_create();
2140 if (!msg)
2141 return ENOENT;
2142
2143 msg->dataptr = &result;
2144 msg->wdata.active = true;
2145
2146 ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, arg4,
2147 msg, reply_received);
2148
2149 int rc;
2150 async_wait_for((aid_t) msg, &rc);
2151
2152 if (rc != EOK)
2153 return rc;
2154
2155 *out_phone = (int) IPC_GET_ARG5(result);
2156 return EOK;
2157}
2158
2159/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2160 *
2161 * Ask through for a new connection to some service.
2162 *
2163 * @param mgmt Exchange management style.
2164 * @param exch Exchange for sending the message.
2165 * @param arg1 User defined argument.
2166 * @param arg2 User defined argument.
2167 * @param arg3 User defined argument.
2168 *
2169 * @return New session on success or NULL on error.
2170 *
2171 */
2172async_sess_t *async_connect_me_to(exch_mgmt_t mgmt, async_exch_t *exch,
2173 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
2174{
2175 if (exch == NULL) {
2176 errno = ENOENT;
2177 return NULL;
2178 }
2179
2180 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2181 if (sess == NULL) {
2182 errno = ENOMEM;
2183 return NULL;
2184 }
2185
2186 int phone;
2187 int rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
2188 0, &phone);
2189 if (rc != EOK) {
2190 errno = rc;
2191 free(sess);
2192 return NULL;
2193 }
2194
2195 sess->iface = 0;
2196 sess->mgmt = mgmt;
2197 sess->phone = phone;
2198 sess->arg1 = arg1;
2199 sess->arg2 = arg2;
2200 sess->arg3 = arg3;
2201
2202 fibril_mutex_initialize(&sess->remote_state_mtx);
2203 sess->remote_state_data = NULL;
2204
2205 list_initialize(&sess->exch_list);
2206 fibril_mutex_initialize(&sess->mutex);
2207 atomic_set(&sess->refcnt, 0);
2208
2209 return sess;
2210}
2211
2212/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2213 *
2214 * Ask through phone for a new connection to some service and block until
2215 * success.
2216 *
2217 * @param exch Exchange for sending the message.
2218 * @param iface Connection interface.
2219 * @param arg2 User defined argument.
2220 * @param arg3 User defined argument.
2221 *
2222 * @return New session on success or NULL on error.
2223 *
2224 */
2225async_sess_t *async_connect_me_to_iface(async_exch_t *exch, iface_t iface,
2226 sysarg_t arg2, sysarg_t arg3)
2227{
2228 if (exch == NULL) {
2229 errno = ENOENT;
2230 return NULL;
2231 }
2232
2233 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2234 if (sess == NULL) {
2235 errno = ENOMEM;
2236 return NULL;
2237 }
2238
2239 int phone;
2240 int rc = async_connect_me_to_internal(exch->phone, iface, arg2,
2241 arg3, 0, &phone);
2242 if (rc != EOK) {
2243 errno = rc;
2244 free(sess);
2245 return NULL;
2246 }
2247
2248 sess->iface = iface;
2249 sess->phone = phone;
2250 sess->arg1 = iface;
2251 sess->arg2 = arg2;
2252 sess->arg3 = arg3;
2253
2254 fibril_mutex_initialize(&sess->remote_state_mtx);
2255 sess->remote_state_data = NULL;
2256
2257 list_initialize(&sess->exch_list);
2258 fibril_mutex_initialize(&sess->mutex);
2259 atomic_set(&sess->refcnt, 0);
2260
2261 return sess;
2262}
2263
2264/** Set arguments for new connections.
2265 *
2266 * FIXME This is an ugly hack to work around the problem that parallel
2267 * exchanges are implemented using parallel connections. When we create
2268 * a callback session, the framework does not know arguments for the new
2269 * connections.
2270 *
2271 * The proper solution seems to be to implement parallel exchanges using
2272 * tagging.
2273 */
2274void async_sess_args_set(async_sess_t *sess, sysarg_t arg1, sysarg_t arg2,
2275 sysarg_t arg3)
2276{
2277 sess->arg1 = arg1;
2278 sess->arg2 = arg2;
2279 sess->arg3 = arg3;
2280}
2281
2282/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2283 *
2284 * Ask through phone for a new connection to some service and block until
2285 * success.
2286 *
2287 * @param mgmt Exchange management style.
2288 * @param exch Exchange for sending the message.
2289 * @param arg1 User defined argument.
2290 * @param arg2 User defined argument.
2291 * @param arg3 User defined argument.
2292 *
2293 * @return New session on success or NULL on error.
2294 *
2295 */
2296async_sess_t *async_connect_me_to_blocking(exch_mgmt_t mgmt, async_exch_t *exch,
2297 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
2298{
2299 if (exch == NULL) {
2300 errno = ENOENT;
2301 return NULL;
2302 }
2303
2304 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2305 if (sess == NULL) {
2306 errno = ENOMEM;
2307 return NULL;
2308 }
2309
2310 int phone;
2311 int rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
2312 IPC_FLAG_BLOCKING, &phone);
2313
2314 if (rc != EOK) {
2315 errno = rc;
2316 free(sess);
2317 return NULL;
2318 }
2319
2320 sess->iface = 0;
2321 sess->mgmt = mgmt;
2322 sess->phone = phone;
2323 sess->arg1 = arg1;
2324 sess->arg2 = arg2;
2325 sess->arg3 = arg3;
2326
2327 fibril_mutex_initialize(&sess->remote_state_mtx);
2328 sess->remote_state_data = NULL;
2329
2330 list_initialize(&sess->exch_list);
2331 fibril_mutex_initialize(&sess->mutex);
2332 atomic_set(&sess->refcnt, 0);
2333
2334 return sess;
2335}
2336
2337/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
2338 *
2339 * Ask through phone for a new connection to some service and block until
2340 * success.
2341 *
2342 * @param exch Exchange for sending the message.
2343 * @param iface Connection interface.
2344 * @param arg2 User defined argument.
2345 * @param arg3 User defined argument.
2346 *
2347 * @return New session on success or NULL on error.
2348 *
2349 */
2350async_sess_t *async_connect_me_to_blocking_iface(async_exch_t *exch, iface_t iface,
2351 sysarg_t arg2, sysarg_t arg3)
2352{
2353 if (exch == NULL) {
2354 errno = ENOENT;
2355 return NULL;
2356 }
2357
2358 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2359 if (sess == NULL) {
2360 errno = ENOMEM;
2361 return NULL;
2362 }
2363
2364 int phone;
2365 int rc = async_connect_me_to_internal(exch->phone, iface, arg2,
2366 arg3, IPC_FLAG_BLOCKING, &phone);
2367 if (rc != EOK) {
2368 errno = rc;
2369 free(sess);
2370 return NULL;
2371 }
2372
2373 sess->iface = iface;
2374 sess->phone = phone;
2375 sess->arg1 = iface;
2376 sess->arg2 = arg2;
2377 sess->arg3 = arg3;
2378
2379 fibril_mutex_initialize(&sess->remote_state_mtx);
2380 sess->remote_state_data = NULL;
2381
2382 list_initialize(&sess->exch_list);
2383 fibril_mutex_initialize(&sess->mutex);
2384 atomic_set(&sess->refcnt, 0);
2385
2386 return sess;
2387}
2388
2389/** Connect to a task specified by id.
2390 *
2391 */
2392async_sess_t *async_connect_kbox(task_id_t id)
2393{
2394 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
2395 if (sess == NULL) {
2396 errno = ENOMEM;
2397 return NULL;
2398 }
2399
2400 cap_handle_t phone;
2401 int rc = ipc_connect_kbox(id, &phone);
2402 if (rc != EOK) {
2403 errno = rc;
2404 free(sess);
2405 return NULL;
2406 }
2407
2408 sess->iface = 0;
2409 sess->mgmt = EXCHANGE_ATOMIC;
2410 sess->phone = phone;
2411 sess->arg1 = 0;
2412 sess->arg2 = 0;
2413 sess->arg3 = 0;
2414
2415 fibril_mutex_initialize(&sess->remote_state_mtx);
2416 sess->remote_state_data = NULL;
2417
2418 list_initialize(&sess->exch_list);
2419 fibril_mutex_initialize(&sess->mutex);
2420 atomic_set(&sess->refcnt, 0);
2421
2422 return sess;
2423}
2424
2425static int async_hangup_internal(int phone)
2426{
2427 return ipc_hangup(phone);
2428}
2429
2430/** Wrapper for ipc_hangup.
2431 *
2432 * @param sess Session to hung up.
2433 *
2434 * @return Zero on success or a negative error code.
2435 *
2436 */
2437int async_hangup(async_sess_t *sess)
2438{
2439 async_exch_t *exch;
2440
2441 assert(sess);
2442
2443 if (atomic_get(&sess->refcnt) > 0)
2444 return EBUSY;
2445
2446 fibril_mutex_lock(&async_sess_mutex);
2447
2448 int rc = async_hangup_internal(sess->phone);
2449
2450 while (!list_empty(&sess->exch_list)) {
2451 exch = (async_exch_t *)
2452 list_get_instance(list_first(&sess->exch_list),
2453 async_exch_t, sess_link);
2454
2455 list_remove(&exch->sess_link);
2456 list_remove(&exch->global_link);
2457 async_hangup_internal(exch->phone);
2458 free(exch);
2459 }
2460
2461 free(sess);
2462
2463 fibril_mutex_unlock(&async_sess_mutex);
2464
2465 return rc;
2466}
2467
2468/** Interrupt one thread of this task from waiting for IPC. */
2469void async_poke(void)
2470{
2471 ipc_poke();
2472}
2473
2474/** Start new exchange in a session.
2475 *
2476 * @param session Session.
2477 *
2478 * @return New exchange or NULL on error.
2479 *
2480 */
2481async_exch_t *async_exchange_begin(async_sess_t *sess)
2482{
2483 if (sess == NULL)
2484 return NULL;
2485
2486 exch_mgmt_t mgmt = sess->mgmt;
2487 if (sess->iface != 0)
2488 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
2489
2490 async_exch_t *exch = NULL;
2491
2492 fibril_mutex_lock(&async_sess_mutex);
2493
2494 if (!list_empty(&sess->exch_list)) {
2495 /*
2496 * There are inactive exchanges in the session.
2497 */
2498 exch = (async_exch_t *)
2499 list_get_instance(list_first(&sess->exch_list),
2500 async_exch_t, sess_link);
2501
2502 list_remove(&exch->sess_link);
2503 list_remove(&exch->global_link);
2504 } else {
2505 /*
2506 * There are no available exchanges in the session.
2507 */
2508
2509 if ((mgmt == EXCHANGE_ATOMIC) ||
2510 (mgmt == EXCHANGE_SERIALIZE)) {
2511 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
2512 if (exch != NULL) {
2513 link_initialize(&exch->sess_link);
2514 link_initialize(&exch->global_link);
2515 exch->sess = sess;
2516 exch->phone = sess->phone;
2517 }
2518 } else if (mgmt == EXCHANGE_PARALLEL) {
2519 int phone;
2520 int rc;
2521
2522 retry:
2523 /*
2524 * Make a one-time attempt to connect a new data phone.
2525 */
2526 rc = async_connect_me_to_internal(sess->phone, sess->arg1,
2527 sess->arg2, sess->arg3, 0, &phone);
2528 if (rc == EOK) {
2529 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
2530 if (exch != NULL) {
2531 link_initialize(&exch->sess_link);
2532 link_initialize(&exch->global_link);
2533 exch->sess = sess;
2534 exch->phone = phone;
2535 } else
2536 async_hangup_internal(phone);
2537 } else if (!list_empty(&inactive_exch_list)) {
2538 /*
2539 * We did not manage to connect a new phone. But we
2540 * can try to close some of the currently inactive
2541 * connections in other sessions and try again.
2542 */
2543 exch = (async_exch_t *)
2544 list_get_instance(list_first(&inactive_exch_list),
2545 async_exch_t, global_link);
2546
2547 list_remove(&exch->sess_link);
2548 list_remove(&exch->global_link);
2549 async_hangup_internal(exch->phone);
2550 free(exch);
2551 goto retry;
2552 } else {
2553 /*
2554 * Wait for a phone to become available.
2555 */
2556 fibril_condvar_wait(&avail_phone_cv, &async_sess_mutex);
2557 goto retry;
2558 }
2559 }
2560 }
2561
2562 fibril_mutex_unlock(&async_sess_mutex);
2563
2564 if (exch != NULL) {
2565 atomic_inc(&sess->refcnt);
2566
2567 if (mgmt == EXCHANGE_SERIALIZE)
2568 fibril_mutex_lock(&sess->mutex);
2569 }
2570
2571 return exch;
2572}
2573
2574/** Finish an exchange.
2575 *
2576 * @param exch Exchange to finish.
2577 *
2578 */
2579void async_exchange_end(async_exch_t *exch)
2580{
2581 if (exch == NULL)
2582 return;
2583
2584 async_sess_t *sess = exch->sess;
2585 assert(sess != NULL);
2586
2587 exch_mgmt_t mgmt = sess->mgmt;
2588 if (sess->iface != 0)
2589 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
2590
2591 atomic_dec(&sess->refcnt);
2592
2593 if (mgmt == EXCHANGE_SERIALIZE)
2594 fibril_mutex_unlock(&sess->mutex);
2595
2596 fibril_mutex_lock(&async_sess_mutex);
2597
2598 list_append(&exch->sess_link, &sess->exch_list);
2599 list_append(&exch->global_link, &inactive_exch_list);
2600 fibril_condvar_signal(&avail_phone_cv);
2601
2602 fibril_mutex_unlock(&async_sess_mutex);
2603}
2604
2605/** Wrapper for IPC_M_SHARE_IN calls using the async framework.
2606 *
2607 * @param exch Exchange for sending the message.
2608 * @param size Size of the destination address space area.
2609 * @param arg User defined argument.
2610 * @param flags Storage for the received flags. Can be NULL.
2611 * @param dst Address of the storage for the destination address space area
2612 * base address. Cannot be NULL.
2613 *
2614 * @return Zero on success or a negative error code from errno.h.
2615 *
2616 */
2617int async_share_in_start(async_exch_t *exch, size_t size, sysarg_t arg,
2618 unsigned int *flags, void **dst)
2619{
2620 if (exch == NULL)
2621 return ENOENT;
2622
2623 sysarg_t _flags = 0;
2624 sysarg_t _dst = (sysarg_t) -1;
2625 int res = async_req_2_4(exch, IPC_M_SHARE_IN, (sysarg_t) size,
2626 arg, NULL, &_flags, NULL, &_dst);
2627
2628 if (flags)
2629 *flags = (unsigned int) _flags;
2630
2631 *dst = (void *) _dst;
2632 return res;
2633}
2634
2635/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
2636 *
2637 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN
2638 * calls so that the user doesn't have to remember the meaning of each IPC
2639 * argument.
2640 *
2641 * So far, this wrapper is to be used from within a connection fibril.
2642 *
2643 * @param chandle Storage for the handle of the IPC_M_SHARE_IN call.
2644 * @param size Destination address space area size.
2645 *
2646 * @return True on success, false on failure.
2647 *
2648 */
2649bool async_share_in_receive(cap_handle_t *chandle, size_t *size)
2650{
2651 assert(chandle);
2652 assert(size);
2653
2654 ipc_call_t data;
2655 *chandle = async_get_call(&data);
2656
2657 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
2658 return false;
2659
2660 *size = (size_t) IPC_GET_ARG1(data);
2661 return true;
2662}
2663
2664/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
2665 *
2666 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
2667 * calls so that the user doesn't have to remember the meaning of each IPC
2668 * argument.
2669 *
2670 * @param chandle Handle of the IPC_M_DATA_READ call to answer.
2671 * @param src Source address space base.
2672 * @param flags Flags to be used for sharing. Bits can be only cleared.
2673 *
2674 * @return Zero on success or a value from @ref errno.h on failure.
2675 *
2676 */
2677int async_share_in_finalize(cap_handle_t chandle, void *src, unsigned int flags)
2678{
2679 return ipc_answer_3(chandle, EOK, (sysarg_t) src, (sysarg_t) flags,
2680 (sysarg_t) __entry);
2681}
2682
2683/** Wrapper for IPC_M_SHARE_OUT calls using the async framework.
2684 *
2685 * @param exch Exchange for sending the message.
2686 * @param src Source address space area base address.
2687 * @param flags Flags to be used for sharing. Bits can be only cleared.
2688 *
2689 * @return Zero on success or a negative error code from errno.h.
2690 *
2691 */
2692int async_share_out_start(async_exch_t *exch, void *src, unsigned int flags)
2693{
2694 if (exch == NULL)
2695 return ENOENT;
2696
2697 return async_req_3_0(exch, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
2698 (sysarg_t) flags);
2699}
2700
2701/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
2702 *
2703 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT
2704 * calls so that the user doesn't have to remember the meaning of each IPC
2705 * argument.
2706 *
2707 * So far, this wrapper is to be used from within a connection fibril.
2708 *
2709 * @param chandle Storage for the hash of the IPC_M_SHARE_OUT call.
2710 * @param size Storage for the source address space area size.
2711 * @param flags Storage for the sharing flags.
2712 *
2713 * @return True on success, false on failure.
2714 *
2715 */
2716bool async_share_out_receive(cap_handle_t *chandle, size_t *size,
2717 unsigned int *flags)
2718{
2719 assert(chandle);
2720 assert(size);
2721 assert(flags);
2722
2723 ipc_call_t data;
2724 *chandle = async_get_call(&data);
2725
2726 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
2727 return false;
2728
2729 *size = (size_t) IPC_GET_ARG2(data);
2730 *flags = (unsigned int) IPC_GET_ARG3(data);
2731 return true;
2732}
2733
2734/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
2735 *
2736 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
2737 * calls so that the user doesn't have to remember the meaning of each IPC
2738 * argument.
2739 *
2740 * @param chandle Handle of the IPC_M_DATA_WRITE call to answer.
2741 * @param dst Address of the storage for the destination address space area
2742 * base address.
2743 *
2744 * @return Zero on success or a value from @ref errno.h on failure.
2745 *
2746 */
2747int async_share_out_finalize(cap_handle_t chandle, void **dst)
2748{
2749 return ipc_answer_2(chandle, EOK, (sysarg_t) __entry, (sysarg_t) dst);
2750}
2751
2752/** Start IPC_M_DATA_READ using the async framework.
2753 *
2754 * @param exch Exchange for sending the message.
2755 * @param dst Address of the beginning of the destination buffer.
2756 * @param size Size of the destination buffer (in bytes).
2757 * @param dataptr Storage of call data (arg 2 holds actual data size).
2758 *
2759 * @return Hash of the sent message or 0 on error.
2760 *
2761 */
2762aid_t async_data_read(async_exch_t *exch, void *dst, size_t size,
2763 ipc_call_t *dataptr)
2764{
2765 return async_send_2(exch, IPC_M_DATA_READ, (sysarg_t) dst,
2766 (sysarg_t) size, dataptr);
2767}
2768
2769/** Wrapper for IPC_M_DATA_READ calls using the async framework.
2770 *
2771 * @param exch Exchange for sending the message.
2772 * @param dst Address of the beginning of the destination buffer.
2773 * @param size Size of the destination buffer.
2774 *
2775 * @return Zero on success or a negative error code from errno.h.
2776 *
2777 */
2778int async_data_read_start(async_exch_t *exch, void *dst, size_t size)
2779{
2780 if (exch == NULL)
2781 return ENOENT;
2782
2783 return async_req_2_0(exch, IPC_M_DATA_READ, (sysarg_t) dst,
2784 (sysarg_t) size);
2785}
2786
2787/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
2788 *
2789 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
2790 * calls so that the user doesn't have to remember the meaning of each IPC
2791 * argument.
2792 *
2793 * So far, this wrapper is to be used from within a connection fibril.
2794 *
2795 * @param chandle Storage for the handle of the IPC_M_DATA_READ.
2796 * @param size Storage for the maximum size. Can be NULL.
2797 *
2798 * @return True on success, false on failure.
2799 *
2800 */
2801bool async_data_read_receive(cap_handle_t *chandle, size_t *size)
2802{
2803 ipc_call_t data;
2804 return async_data_read_receive_call(chandle, &data, size);
2805}
2806
2807/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
2808 *
2809 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
2810 * calls so that the user doesn't have to remember the meaning of each IPC
2811 * argument.
2812 *
2813 * So far, this wrapper is to be used from within a connection fibril.
2814 *
2815 * @param chandle Storage for the handle of the IPC_M_DATA_READ.
2816 * @param size Storage for the maximum size. Can be NULL.
2817 *
2818 * @return True on success, false on failure.
2819 *
2820 */
2821bool async_data_read_receive_call(cap_handle_t *chandle, ipc_call_t *data,
2822 size_t *size)
2823{
2824 assert(chandle);
2825 assert(data);
2826
2827 *chandle = async_get_call(data);
2828
2829 if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_READ)
2830 return false;
2831
2832 if (size)
2833 *size = (size_t) IPC_GET_ARG2(*data);
2834
2835 return true;
2836}
2837
2838/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
2839 *
2840 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
2841 * calls so that the user doesn't have to remember the meaning of each IPC
2842 * argument.
2843 *
2844 * @param chandle Handle of the IPC_M_DATA_READ call to answer.
2845 * @param src Source address for the IPC_M_DATA_READ call.
2846 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
2847 * the maximum size announced by the sender.
2848 *
2849 * @return Zero on success or a value from @ref errno.h on failure.
2850 *
2851 */
2852int async_data_read_finalize(cap_handle_t chandle, const void *src, size_t size)
2853{
2854 return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) size);
2855}
2856
2857/** Wrapper for forwarding any read request
2858 *
2859 */
2860int async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
2861 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
2862 ipc_call_t *dataptr)
2863{
2864 if (exch == NULL)
2865 return ENOENT;
2866
2867 cap_handle_t chandle;
2868 if (!async_data_read_receive(&chandle, NULL)) {
2869 ipc_answer_0(chandle, EINVAL);
2870 return EINVAL;
2871 }
2872
2873 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
2874 dataptr);
2875 if (msg == 0) {
2876 ipc_answer_0(chandle, EINVAL);
2877 return EINVAL;
2878 }
2879
2880 int retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
2881 IPC_FF_ROUTE_FROM_ME);
2882 if (retval != EOK) {
2883 async_forget(msg);
2884 ipc_answer_0(chandle, retval);
2885 return retval;
2886 }
2887
2888 int rc;
2889 async_wait_for(msg, &rc);
2890
2891 return (int) rc;
2892}
2893
2894/** Wrapper for IPC_M_DATA_WRITE calls using the async framework.
2895 *
2896 * @param exch Exchange for sending the message.
2897 * @param src Address of the beginning of the source buffer.
2898 * @param size Size of the source buffer.
2899 *
2900 * @return Zero on success or a negative error code from errno.h.
2901 *
2902 */
2903int async_data_write_start(async_exch_t *exch, const void *src, size_t size)
2904{
2905 if (exch == NULL)
2906 return ENOENT;
2907
2908 return async_req_2_0(exch, IPC_M_DATA_WRITE, (sysarg_t) src,
2909 (sysarg_t) size);
2910}
2911
2912/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
2913 *
2914 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
2915 * calls so that the user doesn't have to remember the meaning of each IPC
2916 * argument.
2917 *
2918 * So far, this wrapper is to be used from within a connection fibril.
2919 *
2920 * @param chandle Storage for the handle of the IPC_M_DATA_WRITE.
2921 * @param size Storage for the suggested size. May be NULL.
2922 *
2923 * @return True on success, false on failure.
2924 *
2925 */
2926bool async_data_write_receive(cap_handle_t *chandle, size_t *size)
2927{
2928 ipc_call_t data;
2929 return async_data_write_receive_call(chandle, &data, size);
2930}
2931
2932/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
2933 *
2934 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
2935 * calls so that the user doesn't have to remember the meaning of each IPC
2936 * argument.
2937 *
2938 * So far, this wrapper is to be used from within a connection fibril.
2939 *
2940 * @param chandle Storage for the handle of the IPC_M_DATA_WRITE.
2941 * @param data Storage for the ipc call data.
2942 * @param size Storage for the suggested size. May be NULL.
2943 *
2944 * @return True on success, false on failure.
2945 *
2946 */
2947bool async_data_write_receive_call(cap_handle_t *chandle, ipc_call_t *data,
2948 size_t *size)
2949{
2950 assert(chandle);
2951 assert(data);
2952
2953 *chandle = async_get_call(data);
2954
2955 if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_WRITE)
2956 return false;
2957
2958 if (size)
2959 *size = (size_t) IPC_GET_ARG2(*data);
2960
2961 return true;
2962}
2963
2964/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
2965 *
2966 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
2967 * calls so that the user doesn't have to remember the meaning of each IPC
2968 * argument.
2969 *
2970 * @param chandle Handle of the IPC_M_DATA_WRITE call to answer.
2971 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
2972 * @param size Final size for the IPC_M_DATA_WRITE call.
2973 *
2974 * @return Zero on success or a value from @ref errno.h on failure.
2975 *
2976 */
2977int async_data_write_finalize(cap_handle_t chandle, void *dst, size_t size)
2978{
2979 return ipc_answer_2(chandle, EOK, (sysarg_t) dst, (sysarg_t) size);
2980}
2981
2982/** Wrapper for receiving binary data or strings
2983 *
2984 * This wrapper only makes it more comfortable to use async_data_write_*
2985 * functions to receive binary data or strings.
2986 *
2987 * @param data Pointer to data pointer (which should be later disposed
2988 * by free()). If the operation fails, the pointer is not
2989 * touched.
2990 * @param nullterm If true then the received data is always zero terminated.
2991 * This also causes to allocate one extra byte beyond the
2992 * raw transmitted data.
2993 * @param min_size Minimum size (in bytes) of the data to receive.
2994 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
2995 * no limit.
2996 * @param granulariy If non-zero then the size of the received data has to
2997 * be divisible by this value.
2998 * @param received If not NULL, the size of the received data is stored here.
2999 *
3000 * @return Zero on success or a value from @ref errno.h on failure.
3001 *
3002 */
3003int async_data_write_accept(void **data, const bool nullterm,
3004 const size_t min_size, const size_t max_size, const size_t granularity,
3005 size_t *received)
3006{
3007 assert(data);
3008
3009 cap_handle_t chandle;
3010 size_t size;
3011 if (!async_data_write_receive(&chandle, &size)) {
3012 ipc_answer_0(chandle, EINVAL);
3013 return EINVAL;
3014 }
3015
3016 if (size < min_size) {
3017 ipc_answer_0(chandle, EINVAL);
3018 return EINVAL;
3019 }
3020
3021 if ((max_size > 0) && (size > max_size)) {
3022 ipc_answer_0(chandle, EINVAL);
3023 return EINVAL;
3024 }
3025
3026 if ((granularity > 0) && ((size % granularity) != 0)) {
3027 ipc_answer_0(chandle, EINVAL);
3028 return EINVAL;
3029 }
3030
3031 void *arg_data;
3032
3033 if (nullterm)
3034 arg_data = malloc(size + 1);
3035 else
3036 arg_data = malloc(size);
3037
3038 if (arg_data == NULL) {
3039 ipc_answer_0(chandle, ENOMEM);
3040 return ENOMEM;
3041 }
3042
3043 int rc = async_data_write_finalize(chandle, arg_data, size);
3044 if (rc != EOK) {
3045 free(arg_data);
3046 return rc;
3047 }
3048
3049 if (nullterm)
3050 ((char *) arg_data)[size] = 0;
3051
3052 *data = arg_data;
3053 if (received != NULL)
3054 *received = size;
3055
3056 return EOK;
3057}
3058
3059/** Wrapper for voiding any data that is about to be received
3060 *
3061 * This wrapper can be used to void any pending data
3062 *
3063 * @param retval Error value from @ref errno.h to be returned to the caller.
3064 *
3065 */
3066void async_data_write_void(int retval)
3067{
3068 cap_handle_t chandle;
3069 async_data_write_receive(&chandle, NULL);
3070 ipc_answer_0(chandle, retval);
3071}
3072
3073/** Wrapper for forwarding any data that is about to be received
3074 *
3075 */
3076int async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
3077 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
3078 ipc_call_t *dataptr)
3079{
3080 if (exch == NULL)
3081 return ENOENT;
3082
3083 cap_handle_t chandle;
3084 if (!async_data_write_receive(&chandle, NULL)) {
3085 ipc_answer_0(chandle, EINVAL);
3086 return EINVAL;
3087 }
3088
3089 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
3090 dataptr);
3091 if (msg == 0) {
3092 ipc_answer_0(chandle, EINVAL);
3093 return EINVAL;
3094 }
3095
3096 int retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
3097 IPC_FF_ROUTE_FROM_ME);
3098 if (retval != EOK) {
3099 async_forget(msg);
3100 ipc_answer_0(chandle, retval);
3101 return retval;
3102 }
3103
3104 int rc;
3105 async_wait_for(msg, &rc);
3106
3107 return (int) rc;
3108}
3109
3110/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
3111 *
3112 * If the current call is IPC_M_CONNECT_TO_ME then a new
3113 * async session is created for the accepted phone.
3114 *
3115 * @param mgmt Exchange management style.
3116 *
3117 * @return New async session.
3118 * @return NULL on failure.
3119 *
3120 */
3121async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
3122{
3123 /* Accept the phone */
3124 ipc_call_t call;
3125 cap_handle_t chandle = async_get_call(&call);
3126 cap_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(call);
3127
3128 if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) || (phandle < 0)) {
3129 async_answer_0(chandle, EINVAL);
3130 return NULL;
3131 }
3132
3133 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
3134 if (sess == NULL) {
3135 async_answer_0(chandle, ENOMEM);
3136 return NULL;
3137 }
3138
3139 sess->iface = 0;
3140 sess->mgmt = mgmt;
3141 sess->phone = phandle;
3142 sess->arg1 = 0;
3143 sess->arg2 = 0;
3144 sess->arg3 = 0;
3145
3146 fibril_mutex_initialize(&sess->remote_state_mtx);
3147 sess->remote_state_data = NULL;
3148
3149 list_initialize(&sess->exch_list);
3150 fibril_mutex_initialize(&sess->mutex);
3151 atomic_set(&sess->refcnt, 0);
3152
3153 /* Acknowledge the connected phone */
3154 async_answer_0(chandle, EOK);
3155
3156 return sess;
3157}
3158
3159/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
3160 *
3161 * If the call is IPC_M_CONNECT_TO_ME then a new
3162 * async session is created. However, the phone is
3163 * not accepted automatically.
3164 *
3165 * @param mgmt Exchange management style.
3166 * @param call Call data.
3167 *
3168 * @return New async session.
3169 * @return NULL on failure.
3170 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
3171 *
3172 */
3173async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
3174{
3175 cap_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(*call);
3176
3177 if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) || (phandle < 0))
3178 return NULL;
3179
3180 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
3181 if (sess == NULL)
3182 return NULL;
3183
3184 sess->iface = 0;
3185 sess->mgmt = mgmt;
3186 sess->phone = phandle;
3187 sess->arg1 = 0;
3188 sess->arg2 = 0;
3189 sess->arg3 = 0;
3190
3191 fibril_mutex_initialize(&sess->remote_state_mtx);
3192 sess->remote_state_data = NULL;
3193
3194 list_initialize(&sess->exch_list);
3195 fibril_mutex_initialize(&sess->mutex);
3196 atomic_set(&sess->refcnt, 0);
3197
3198 return sess;
3199}
3200
3201int async_state_change_start(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
3202 sysarg_t arg3, async_exch_t *other_exch)
3203{
3204 return async_req_5_0(exch, IPC_M_STATE_CHANGE_AUTHORIZE,
3205 arg1, arg2, arg3, 0, other_exch->phone);
3206}
3207
3208bool async_state_change_receive(cap_handle_t *chandle, sysarg_t *arg1,
3209 sysarg_t *arg2, sysarg_t *arg3)
3210{
3211 assert(chandle);
3212
3213 ipc_call_t call;
3214 *chandle = async_get_call(&call);
3215
3216 if (IPC_GET_IMETHOD(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
3217 return false;
3218
3219 if (arg1)
3220 *arg1 = IPC_GET_ARG1(call);
3221 if (arg2)
3222 *arg2 = IPC_GET_ARG2(call);
3223 if (arg3)
3224 *arg3 = IPC_GET_ARG3(call);
3225
3226 return true;
3227}
3228
3229int async_state_change_finalize(cap_handle_t chandle, async_exch_t *other_exch)
3230{
3231 return ipc_answer_1(chandle, EOK, other_exch->phone);
3232}
3233
3234/** Lock and get session remote state
3235 *
3236 * Lock and get the local replica of the remote state
3237 * in stateful sessions. The call should be paired
3238 * with async_remote_state_release*().
3239 *
3240 * @param[in] sess Stateful session.
3241 *
3242 * @return Local replica of the remote state.
3243 *
3244 */
3245void *async_remote_state_acquire(async_sess_t *sess)
3246{
3247 fibril_mutex_lock(&sess->remote_state_mtx);
3248 return sess->remote_state_data;
3249}
3250
3251/** Update the session remote state
3252 *
3253 * Update the local replica of the remote state
3254 * in stateful sessions. The remote state must
3255 * be already locked.
3256 *
3257 * @param[in] sess Stateful session.
3258 * @param[in] state New local replica of the remote state.
3259 *
3260 */
3261void async_remote_state_update(async_sess_t *sess, void *state)
3262{
3263 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
3264 sess->remote_state_data = state;
3265}
3266
3267/** Release the session remote state
3268 *
3269 * Unlock the local replica of the remote state
3270 * in stateful sessions.
3271 *
3272 * @param[in] sess Stateful session.
3273 *
3274 */
3275void async_remote_state_release(async_sess_t *sess)
3276{
3277 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
3278
3279 fibril_mutex_unlock(&sess->remote_state_mtx);
3280}
3281
3282/** Release the session remote state and end an exchange
3283 *
3284 * Unlock the local replica of the remote state
3285 * in stateful sessions. This is convenience function
3286 * which gets the session pointer from the exchange
3287 * and also ends the exchange.
3288 *
3289 * @param[in] exch Stateful session's exchange.
3290 *
3291 */
3292void async_remote_state_release_exchange(async_exch_t *exch)
3293{
3294 if (exch == NULL)
3295 return;
3296
3297 async_sess_t *sess = exch->sess;
3298 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
3299
3300 async_exchange_end(exch);
3301 fibril_mutex_unlock(&sess->remote_state_mtx);
3302}
3303
3304void *async_as_area_create(void *base, size_t size, unsigned int flags,
3305 async_sess_t *pager, sysarg_t id1, sysarg_t id2, sysarg_t id3)
3306{
3307 as_area_pager_info_t pager_info = {
3308 .pager = pager->phone,
3309 .id1 = id1,
3310 .id2 = id2,
3311 .id3 = id3
3312 };
3313 return as_area_create(base, size, flags, &pager_info);
3314}
3315
3316/** @}
3317 */
Note: See TracBrowser for help on using the repository browser.