source: mainline/uspace/lib/c/generic/async.c@ 503ffce

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

Return IPC_CALLID_* in call data instead of callid

Callid will be replaced by capability handles soon so the API needs
to be cleanup up and any flags passed together with callid must be
passed using some other way.

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