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

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

Deduplicate async_create_port().

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