source: mainline/uspace/lib/c/generic/async/server.c@ 8080262

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

Replace remaining explicit uses of futex_t outside fibril implementation, and get rid of async_futex.

  • Property mode set to 100644
File size: 42.1 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(ipc_call_t *icall)
80 * {
81 * if (want_refuse) {
82 * async_answer_0(icall, ELIMIT);
83 * return;
84 * }
85 *
86 * async_answer_0(icall, EOK);
87 *
88 * async_get_call(&call);
89 * somehow_handle_the_call(&call);
90 * async_answer_2(&call, 1, 2, 3);
91 *
92 * async_get_call(&call);
93 * ...
94 * }
95 *
96 */
97
98#define LIBC_ASYNC_C_
99#include <ipc/ipc.h>
100#include <async.h>
101#include "../private/async.h"
102#undef LIBC_ASYNC_C_
103
104#include <ipc/irq.h>
105#include <ipc/event.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 <stdbool.h>
114#include <stdlib.h>
115#include <mem.h>
116#include <stdlib.h>
117#include <macros.h>
118#include <str_error.h>
119#include <as.h>
120#include <abi/mm/as.h>
121#include "../private/libc.h"
122#include "../private/fibril.h"
123
124#define DPRINTF(...) ((void) 0)
125
126/** Call data */
127typedef struct {
128 link_t link;
129 ipc_call_t call;
130} msg_t;
131
132/* Client connection data */
133typedef struct {
134 ht_link_t link;
135
136 task_id_t in_task_id;
137 atomic_t refcnt;
138 void *data;
139} client_t;
140
141/* Server connection data */
142typedef struct {
143 /** Fibril handling the connection. */
144 fid_t fid;
145
146 /** Hash table link. */
147 ht_link_t link;
148
149 /** Incoming client task ID. */
150 task_id_t in_task_id;
151
152 /** Incoming phone hash. */
153 sysarg_t in_phone_hash;
154
155 /** Link to the client tracking structure. */
156 client_t *client;
157
158 /** Message event. */
159 fibril_event_t msg_arrived;
160
161 /** Messages that should be delivered to this fibril. */
162 list_t msg_queue;
163
164 /** Call data of the opening call. */
165 ipc_call_t call;
166
167 /** Identification of the closing call. */
168 cap_call_handle_t close_chandle;
169
170 /** Fibril function that will be used to handle the connection. */
171 async_port_handler_t handler;
172
173 /** Client data */
174 void *data;
175} connection_t;
176
177/* Member of notification_t::msg_list. */
178typedef struct {
179 link_t link;
180 ipc_call_t calldata;
181} notification_msg_t;
182
183/* Notification data */
184typedef struct {
185 /** notification_hash_table link */
186 ht_link_t htlink;
187
188 /** notification_queue link */
189 link_t qlink;
190
191 /** Notification method */
192 sysarg_t imethod;
193
194 /** Notification handler */
195 async_notification_handler_t handler;
196
197 /** Notification handler argument */
198 void *arg;
199
200 /** List of arrived notifications. */
201 list_t msg_list;
202} notification_t;
203
204/** Identifier of the incoming connection handled by the current fibril. */
205static fibril_local connection_t *fibril_connection;
206
207static void *default_client_data_constructor(void)
208{
209 return NULL;
210}
211
212static void default_client_data_destructor(void *data)
213{
214}
215
216static async_client_data_ctor_t async_client_data_create =
217 default_client_data_constructor;
218static async_client_data_dtor_t async_client_data_destroy =
219 default_client_data_destructor;
220
221void async_set_client_data_constructor(async_client_data_ctor_t ctor)
222{
223 assert(async_client_data_create == default_client_data_constructor);
224 async_client_data_create = ctor;
225}
226
227void async_set_client_data_destructor(async_client_data_dtor_t dtor)
228{
229 assert(async_client_data_destroy == default_client_data_destructor);
230 async_client_data_destroy = dtor;
231}
232
233static FIBRIL_RMUTEX_INITIALIZE(client_mutex);
234static hash_table_t client_hash_table;
235
236// TODO: lockfree notification_queue?
237static FIBRIL_RMUTEX_INITIALIZE(notification_mutex);
238static hash_table_t notification_hash_table;
239static LIST_INITIALIZE(notification_queue);
240static FIBRIL_SEMAPHORE_INITIALIZE(notification_semaphore, 0);
241
242static LIST_INITIALIZE(notification_freelist);
243static long notification_freelist_total = 0;
244static long notification_freelist_used = 0;
245
246static sysarg_t notification_avail = 0;
247
248static FIBRIL_RMUTEX_INITIALIZE(conn_mutex);
249static hash_table_t conn_hash_table;
250
251static size_t client_key_hash(void *key)
252{
253 task_id_t in_task_id = *(task_id_t *) key;
254 return in_task_id;
255}
256
257static size_t client_hash(const ht_link_t *item)
258{
259 client_t *client = hash_table_get_inst(item, client_t, link);
260 return client_key_hash(&client->in_task_id);
261}
262
263static bool client_key_equal(void *key, const ht_link_t *item)
264{
265 task_id_t in_task_id = *(task_id_t *) key;
266 client_t *client = hash_table_get_inst(item, client_t, link);
267 return in_task_id == client->in_task_id;
268}
269
270/** Operations for the client hash table. */
271static hash_table_ops_t client_hash_table_ops = {
272 .hash = client_hash,
273 .key_hash = client_key_hash,
274 .key_equal = client_key_equal,
275 .equal = NULL,
276 .remove_callback = NULL
277};
278
279typedef struct {
280 task_id_t task_id;
281 sysarg_t phone_hash;
282} conn_key_t;
283
284/** Compute hash into the connection hash table
285 *
286 * The hash is based on the source task ID and the source phone hash. The task
287 * ID is included in the hash because a phone hash alone might not be unique
288 * while we still track connections for killed tasks due to kernel's recycling
289 * of phone structures.
290 *
291 * @param key Pointer to the connection key structure.
292 *
293 * @return Index into the connection hash table.
294 *
295 */
296static size_t conn_key_hash(void *key)
297{
298 conn_key_t *ck = (conn_key_t *) key;
299
300 size_t hash = 0;
301 hash = hash_combine(hash, LOWER32(ck->task_id));
302 hash = hash_combine(hash, UPPER32(ck->task_id));
303 hash = hash_combine(hash, ck->phone_hash);
304 return hash;
305}
306
307static size_t conn_hash(const ht_link_t *item)
308{
309 connection_t *conn = hash_table_get_inst(item, connection_t, link);
310 return conn_key_hash(&(conn_key_t){
311 .task_id = conn->in_task_id,
312 .phone_hash = conn->in_phone_hash
313 });
314}
315
316static bool conn_key_equal(void *key, const ht_link_t *item)
317{
318 conn_key_t *ck = (conn_key_t *) key;
319 connection_t *conn = hash_table_get_inst(item, connection_t, link);
320 return ((ck->task_id == conn->in_task_id) &&
321 (ck->phone_hash == conn->in_phone_hash));
322}
323
324/** Operations for the connection hash table. */
325static hash_table_ops_t conn_hash_table_ops = {
326 .hash = conn_hash,
327 .key_hash = conn_key_hash,
328 .key_equal = conn_key_equal,
329 .equal = NULL,
330 .remove_callback = NULL
331};
332
333static client_t *async_client_get(task_id_t client_id, bool create)
334{
335 client_t *client = NULL;
336
337 fibril_rmutex_lock(&client_mutex);
338 ht_link_t *link = hash_table_find(&client_hash_table, &client_id);
339 if (link) {
340 client = hash_table_get_inst(link, client_t, link);
341 atomic_inc(&client->refcnt);
342 } else if (create) {
343 // TODO: move the malloc out of critical section
344 /* malloc() is rmutex safe. */
345 client = malloc(sizeof(client_t));
346 if (client) {
347 client->in_task_id = client_id;
348 client->data = async_client_data_create();
349
350 atomic_set(&client->refcnt, 1);
351 hash_table_insert(&client_hash_table, &client->link);
352 }
353 }
354
355 fibril_rmutex_unlock(&client_mutex);
356 return client;
357}
358
359static void async_client_put(client_t *client)
360{
361 bool destroy;
362
363 fibril_rmutex_lock(&client_mutex);
364
365 if (atomic_predec(&client->refcnt) == 0) {
366 hash_table_remove(&client_hash_table, &client->in_task_id);
367 destroy = true;
368 } else
369 destroy = false;
370
371 fibril_rmutex_unlock(&client_mutex);
372
373 if (destroy) {
374 if (client->data)
375 async_client_data_destroy(client->data);
376
377 free(client);
378 }
379}
380
381/** Wrapper for client connection fibril.
382 *
383 * When a new connection arrives, a fibril with this implementing
384 * function is created.
385 *
386 * @param arg Connection structure pointer.
387 *
388 * @return Always zero.
389 *
390 */
391static errno_t connection_fibril(void *arg)
392{
393 assert(arg);
394
395 /*
396 * Setup fibril-local connection pointer.
397 */
398 fibril_connection = (connection_t *) arg;
399
400 /*
401 * Add our reference for the current connection in the client task
402 * tracking structure. If this is the first reference, create and
403 * hash in a new tracking structure.
404 */
405
406 client_t *client = async_client_get(fibril_connection->in_task_id, true);
407 if (!client) {
408 ipc_answer_0(fibril_connection->call.cap_handle, ENOMEM);
409 return 0;
410 }
411
412 fibril_connection->client = client;
413
414 /*
415 * Call the connection handler function.
416 */
417 fibril_connection->handler(&fibril_connection->call,
418 fibril_connection->data);
419
420 /*
421 * Remove the reference for this client task connection.
422 */
423 async_client_put(client);
424
425 /*
426 * Remove myself from the connection hash table.
427 */
428 fibril_rmutex_lock(&conn_mutex);
429 hash_table_remove(&conn_hash_table, &(conn_key_t){
430 .task_id = fibril_connection->in_task_id,
431 .phone_hash = fibril_connection->in_phone_hash
432 });
433 fibril_rmutex_unlock(&conn_mutex);
434
435 /*
436 * Answer all remaining messages with EHANGUP.
437 */
438 while (!list_empty(&fibril_connection->msg_queue)) {
439 msg_t *msg =
440 list_get_instance(list_first(&fibril_connection->msg_queue),
441 msg_t, link);
442
443 list_remove(&msg->link);
444 ipc_answer_0(msg->call.cap_handle, EHANGUP);
445 free(msg);
446 }
447
448 /*
449 * If the connection was hung-up, answer the last call,
450 * i.e. IPC_M_PHONE_HUNGUP.
451 */
452 if (fibril_connection->close_chandle)
453 ipc_answer_0(fibril_connection->close_chandle, EOK);
454
455 free(fibril_connection);
456 return EOK;
457}
458
459/** Create a new fibril for a new connection.
460 *
461 * Create new fibril for connection, fill in connection structures and insert it
462 * into the hash table, so that later we can easily do routing of messages to
463 * particular fibrils.
464 *
465 * @param in_task_id Identification of the incoming connection.
466 * @param in_phone_hash Identification of the incoming connection.
467 * @param call Call data of the opening call. If call is NULL,
468 * the connection was opened by accepting the
469 * IPC_M_CONNECT_TO_ME call and this function is
470 * called directly by the server.
471 * @param handler Connection handler.
472 * @param data Client argument to pass to the connection handler.
473 *
474 * @return New fibril id or NULL on failure.
475 *
476 */
477static fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
478 ipc_call_t *call, async_port_handler_t handler, void *data)
479{
480 connection_t *conn = malloc(sizeof(*conn));
481 if (!conn) {
482 if (call)
483 ipc_answer_0(call->cap_handle, ENOMEM);
484
485 return (fid_t) NULL;
486 }
487
488 conn->in_task_id = in_task_id;
489 conn->in_phone_hash = in_phone_hash;
490 conn->msg_arrived = FIBRIL_EVENT_INIT;
491 list_initialize(&conn->msg_queue);
492 conn->close_chandle = CAP_NIL;
493 conn->handler = handler;
494 conn->data = data;
495
496 if (call)
497 conn->call = *call;
498 else
499 conn->call.cap_handle = CAP_NIL;
500
501 /* We will activate the fibril ASAP */
502 conn->fid = fibril_create(connection_fibril, conn);
503
504 if (conn->fid == 0) {
505 free(conn);
506
507 if (call)
508 ipc_answer_0(call->cap_handle, ENOMEM);
509
510 return (fid_t) NULL;
511 }
512
513 /* Add connection to the connection hash table */
514
515 fibril_rmutex_lock(&conn_mutex);
516 hash_table_insert(&conn_hash_table, &conn->link);
517 fibril_rmutex_unlock(&conn_mutex);
518
519 fibril_start(conn->fid);
520
521 return conn->fid;
522}
523
524/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
525 *
526 * Ask through phone for a new connection to some service.
527 *
528 * @param exch Exchange for sending the message.
529 * @param iface Callback interface.
530 * @param arg1 User defined argument.
531 * @param arg2 User defined argument.
532 * @param handler Callback handler.
533 * @param data Handler data.
534 * @param port_id ID of the newly created port.
535 *
536 * @return Zero on success or an error code.
537 *
538 */
539errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
540 sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
541{
542 if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
543 return EINVAL;
544
545 if (exch == NULL)
546 return ENOENT;
547
548 ipc_call_t answer;
549 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
550 &answer);
551
552 errno_t rc;
553 async_wait_for(req, &rc);
554 if (rc != EOK)
555 return rc;
556
557 rc = async_create_port_internal(iface, handler, data, port_id);
558 if (rc != EOK)
559 return rc;
560
561 sysarg_t phone_hash = IPC_GET_ARG5(answer);
562 fid_t fid = async_new_connection(answer.in_task_id, phone_hash,
563 NULL, handler, data);
564 if (fid == (fid_t) NULL)
565 return ENOMEM;
566
567 return EOK;
568}
569
570static size_t notification_key_hash(void *key)
571{
572 sysarg_t id = *(sysarg_t *) key;
573 return id;
574}
575
576static size_t notification_hash(const ht_link_t *item)
577{
578 notification_t *notification =
579 hash_table_get_inst(item, notification_t, htlink);
580 return notification_key_hash(&notification->imethod);
581}
582
583static bool notification_key_equal(void *key, const ht_link_t *item)
584{
585 sysarg_t id = *(sysarg_t *) key;
586 notification_t *notification =
587 hash_table_get_inst(item, notification_t, htlink);
588 return id == notification->imethod;
589}
590
591/** Operations for the notification hash table. */
592static hash_table_ops_t notification_hash_table_ops = {
593 .hash = notification_hash,
594 .key_hash = notification_key_hash,
595 .key_equal = notification_key_equal,
596 .equal = NULL,
597 .remove_callback = NULL
598};
599
600/** Try to route a call to an appropriate connection fibril.
601 *
602 * If the proper connection fibril is found, a message with the call is added to
603 * its message queue. If the fibril was not active, it is activated and all
604 * timeouts are unregistered.
605 *
606 * @param call Data of the incoming call.
607 *
608 * @return False if the call doesn't match any connection.
609 * @return True if the call was passed to the respective connection fibril.
610 *
611 */
612static bool route_call(ipc_call_t *call)
613{
614 assert(call);
615
616 fibril_rmutex_lock(&conn_mutex);
617
618 ht_link_t *link = hash_table_find(&conn_hash_table, &(conn_key_t){
619 .task_id = call->in_task_id,
620 .phone_hash = call->in_phone_hash
621 });
622 if (!link) {
623 fibril_rmutex_unlock(&conn_mutex);
624 return false;
625 }
626
627 connection_t *conn = hash_table_get_inst(link, connection_t, link);
628
629 // FIXME: malloc in critical section
630 msg_t *msg = malloc(sizeof(*msg));
631 if (!msg) {
632 fibril_rmutex_unlock(&conn_mutex);
633 return false;
634 }
635
636 msg->call = *call;
637 list_append(&msg->link, &conn->msg_queue);
638
639 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
640 conn->close_chandle = call->cap_handle;
641
642 fibril_rmutex_unlock(&conn_mutex);
643
644 /* If the connection fibril is waiting for an event, activate it */
645 fibril_notify(&conn->msg_arrived);
646 return true;
647}
648
649/** Function implementing the notification handler fibril. Never returns. */
650static errno_t notification_fibril_func(void *arg)
651{
652 (void) arg;
653
654 while (true) {
655 fibril_semaphore_down(&notification_semaphore);
656
657 fibril_rmutex_lock(&notification_mutex);
658
659 /*
660 * The semaphore ensures that if we get this far,
661 * the queue must be non-empty.
662 */
663 assert(!list_empty(&notification_queue));
664
665 notification_t *notification = list_get_instance(
666 list_first(&notification_queue), notification_t, qlink);
667
668 async_notification_handler_t handler = notification->handler;
669 void *arg = notification->arg;
670
671 notification_msg_t *m = list_pop(&notification->msg_list,
672 notification_msg_t, link);
673 assert(m);
674 ipc_call_t calldata = m->calldata;
675
676 notification_freelist_used--;
677
678 if (notification_freelist_total > 64 &&
679 notification_freelist_total > 2 * notification_freelist_used) {
680 /* Going to free the structure if we have too much. */
681 notification_freelist_total--;
682 } else {
683 /* Otherwise add to freelist. */
684 list_append(&m->link, &notification_freelist);
685 m = NULL;
686 }
687
688 if (list_empty(&notification->msg_list))
689 list_remove(&notification->qlink);
690
691 fibril_rmutex_unlock(&notification_mutex);
692
693 if (handler)
694 handler(&calldata, arg);
695
696 free(m);
697 }
698
699 /* Not reached. */
700 return EOK;
701}
702
703/**
704 * Creates a new dedicated fibril for handling notifications.
705 * By default, there is one such fibril. This function can be used to
706 * create more in order to increase the number of notification that can
707 * be processed concurrently.
708 *
709 * Currently, there is no way to destroy those fibrils after they are created.
710 */
711errno_t async_spawn_notification_handler(void)
712{
713 fid_t f = fibril_create(notification_fibril_func, NULL);
714 if (f == 0)
715 return ENOMEM;
716
717 fibril_add_ready(f);
718 return EOK;
719}
720
721/** Queue notification.
722 *
723 * @param call Data of the incoming call.
724 *
725 */
726static void queue_notification(ipc_call_t *call)
727{
728 assert(call);
729
730 fibril_rmutex_lock(&notification_mutex);
731
732 notification_msg_t *m = list_pop(&notification_freelist,
733 notification_msg_t, link);
734
735 if (!m) {
736 fibril_rmutex_unlock(&notification_mutex);
737 m = malloc(sizeof(notification_msg_t));
738 if (!m) {
739 DPRINTF("Out of memory.\n");
740 abort();
741 }
742
743 fibril_rmutex_lock(&notification_mutex);
744 notification_freelist_total++;
745 }
746
747 ht_link_t *link = hash_table_find(&notification_hash_table,
748 &IPC_GET_IMETHOD(*call));
749 if (!link) {
750 /* Invalid notification. */
751 // TODO: Make sure this can't happen and turn it into assert.
752 notification_freelist_total--;
753 fibril_rmutex_unlock(&notification_mutex);
754 free(m);
755 return;
756 }
757
758 notification_t *notification =
759 hash_table_get_inst(link, notification_t, htlink);
760
761 notification_freelist_used++;
762 m->calldata = *call;
763 list_append(&m->link, &notification->msg_list);
764
765 if (!link_in_use(&notification->qlink))
766 list_append(&notification->qlink, &notification_queue);
767
768 fibril_rmutex_unlock(&notification_mutex);
769
770 fibril_semaphore_up(&notification_semaphore);
771}
772
773/**
774 * Creates a new notification structure and inserts it into the hash table.
775 *
776 * @param handler Function to call when notification is received.
777 * @param arg Argument for the handler function.
778 * @return The newly created notification structure.
779 */
780static notification_t *notification_create(async_notification_handler_t handler, void *arg)
781{
782 notification_t *notification = calloc(1, sizeof(notification_t));
783 if (!notification)
784 return NULL;
785
786 notification->handler = handler;
787 notification->arg = arg;
788
789 list_initialize(&notification->msg_list);
790
791 fid_t fib = 0;
792
793 fibril_rmutex_lock(&notification_mutex);
794
795 if (notification_avail == 0) {
796 /* Attempt to create the first handler fibril. */
797 fib = fibril_create(notification_fibril_func, NULL);
798 if (fib == 0) {
799 fibril_rmutex_unlock(&notification_mutex);
800 free(notification);
801 return NULL;
802 }
803 }
804
805 sysarg_t imethod = notification_avail;
806 notification_avail++;
807
808 notification->imethod = imethod;
809 hash_table_insert(&notification_hash_table, &notification->htlink);
810
811 fibril_rmutex_unlock(&notification_mutex);
812
813 if (imethod == 0) {
814 assert(fib);
815 fibril_add_ready(fib);
816 }
817
818 return notification;
819}
820
821/** Subscribe to IRQ notification.
822 *
823 * @param inr IRQ number.
824 * @param handler Notification handler.
825 * @param data Notification handler client data.
826 * @param ucode Top-half pseudocode handler.
827 *
828 * @param[out] handle IRQ capability handle on success.
829 *
830 * @return An error code.
831 *
832 */
833errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
834 void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
835{
836 notification_t *notification = notification_create(handler, data);
837 if (!notification)
838 return ENOMEM;
839
840 cap_irq_handle_t ihandle;
841 errno_t rc = ipc_irq_subscribe(inr, notification->imethod, ucode,
842 &ihandle);
843 if (rc == EOK && handle != NULL) {
844 *handle = ihandle;
845 }
846 return rc;
847}
848
849/** Unsubscribe from IRQ notification.
850 *
851 * @param handle IRQ capability handle.
852 *
853 * @return Zero on success or an error code.
854 *
855 */
856errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
857{
858 // TODO: Remove entry from hash table
859 // to avoid memory leak
860
861 return ipc_irq_unsubscribe(ihandle);
862}
863
864/** Subscribe to event notifications.
865 *
866 * @param evno Event type to subscribe.
867 * @param handler Notification handler.
868 * @param data Notification handler client data.
869 *
870 * @return Zero on success or an error code.
871 *
872 */
873errno_t async_event_subscribe(event_type_t evno,
874 async_notification_handler_t handler, void *data)
875{
876 notification_t *notification = notification_create(handler, data);
877 if (!notification)
878 return ENOMEM;
879
880 return ipc_event_subscribe(evno, notification->imethod);
881}
882
883/** Subscribe to task event notifications.
884 *
885 * @param evno Event type to subscribe.
886 * @param handler Notification handler.
887 * @param data Notification handler client data.
888 *
889 * @return Zero on success or an error code.
890 *
891 */
892errno_t async_event_task_subscribe(event_task_type_t evno,
893 async_notification_handler_t handler, void *data)
894{
895 notification_t *notification = notification_create(handler, data);
896 if (!notification)
897 return ENOMEM;
898
899 return ipc_event_task_subscribe(evno, notification->imethod);
900}
901
902/** Unmask event notifications.
903 *
904 * @param evno Event type to unmask.
905 *
906 * @return Value returned by the kernel.
907 *
908 */
909errno_t async_event_unmask(event_type_t evno)
910{
911 return ipc_event_unmask(evno);
912}
913
914/** Unmask task event notifications.
915 *
916 * @param evno Event type to unmask.
917 *
918 * @return Value returned by the kernel.
919 *
920 */
921errno_t async_event_task_unmask(event_task_type_t evno)
922{
923 return ipc_event_task_unmask(evno);
924}
925
926/** Return new incoming message for the current (fibril-local) connection.
927 *
928 * @param call Storage where the incoming call data will be stored.
929 * @param usecs Timeout in microseconds. Zero denotes no timeout.
930 *
931 * @return If no timeout was specified, then true is returned.
932 * @return If a timeout is specified, then true is returned unless
933 * the timeout expires prior to receiving a message.
934 *
935 */
936bool async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
937{
938 assert(call);
939 assert(fibril_connection);
940
941 /*
942 * Why doing this?
943 * GCC 4.1.0 coughs on fibril_connection-> dereference.
944 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
945 * I would never expect to find so many errors in
946 * a compiler.
947 */
948 connection_t *conn = fibril_connection;
949
950 struct timeval tv;
951 struct timeval *expires = NULL;
952 if (usecs) {
953 getuptime(&tv);
954 tv_add_diff(&tv, usecs);
955 expires = &tv;
956 }
957
958 fibril_rmutex_lock(&conn_mutex);
959
960 /* If nothing in queue, wait until something arrives */
961 while (list_empty(&conn->msg_queue)) {
962 if (conn->close_chandle) {
963 /*
964 * Handle the case when the connection was already
965 * closed by the client but the server did not notice
966 * the first IPC_M_PHONE_HUNGUP call and continues to
967 * call async_get_call_timeout(). Repeat
968 * IPC_M_PHONE_HUNGUP until the caller notices.
969 */
970 memset(call, 0, sizeof(ipc_call_t));
971 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
972 fibril_rmutex_unlock(&conn_mutex);
973 return true;
974 }
975
976 // TODO: replace with cvar
977 fibril_rmutex_unlock(&conn_mutex);
978
979 errno_t rc = fibril_wait_timeout(&conn->msg_arrived, expires);
980 if (rc == ETIMEOUT)
981 return false;
982
983 fibril_rmutex_lock(&conn_mutex);
984 }
985
986 msg_t *msg = list_get_instance(list_first(&conn->msg_queue),
987 msg_t, link);
988 list_remove(&msg->link);
989
990 *call = msg->call;
991 free(msg);
992
993 fibril_rmutex_unlock(&conn_mutex);
994 return true;
995}
996
997void *async_get_client_data(void)
998{
999 assert(fibril_connection);
1000 return fibril_connection->client->data;
1001}
1002
1003void *async_get_client_data_by_id(task_id_t client_id)
1004{
1005 client_t *client = async_client_get(client_id, false);
1006 if (!client)
1007 return NULL;
1008
1009 if (!client->data) {
1010 async_client_put(client);
1011 return NULL;
1012 }
1013
1014 return client->data;
1015}
1016
1017void async_put_client_data_by_id(task_id_t client_id)
1018{
1019 client_t *client = async_client_get(client_id, false);
1020
1021 assert(client);
1022 assert(client->data);
1023
1024 /* Drop the reference we got in async_get_client_data_by_hash(). */
1025 async_client_put(client);
1026
1027 /* Drop our own reference we got at the beginning of this function. */
1028 async_client_put(client);
1029}
1030
1031/** Handle a call that was received.
1032 *
1033 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
1034 * Otherwise the call is routed to its connection fibril.
1035 *
1036 * @param call Data of the incoming call.
1037 *
1038 */
1039static void handle_call(ipc_call_t *call)
1040{
1041 assert(call);
1042
1043 if (call->flags & IPC_CALL_ANSWERED) {
1044 /* Answer to a call made by us. */
1045 async_reply_received(call);
1046 return;
1047 }
1048
1049 if (call->cap_handle == CAP_NIL) {
1050 if (call->flags & IPC_CALL_NOTIF) {
1051 /* Kernel notification */
1052 queue_notification(call);
1053 }
1054 return;
1055 }
1056
1057 /* New connection */
1058 if (IPC_GET_IMETHOD(*call) == IPC_M_CONNECT_ME_TO) {
1059 iface_t iface = (iface_t) IPC_GET_ARG1(*call);
1060 sysarg_t in_phone_hash = IPC_GET_ARG5(*call);
1061
1062 // TODO: Currently ignores all ports but the first one.
1063 void *data;
1064 async_port_handler_t handler =
1065 async_get_port_handler(iface, 0, &data);
1066
1067 async_new_connection(call->in_task_id, in_phone_hash, call,
1068 handler, data);
1069 return;
1070 }
1071
1072 /* Try to route the call through the connection hash table */
1073 if (route_call(call))
1074 return;
1075
1076 /* Unknown call from unknown phone - hang it up */
1077 ipc_answer_0(call->cap_handle, EHANGUP);
1078}
1079
1080/** Endless loop dispatching incoming calls and answers.
1081 *
1082 * @return Never returns.
1083 *
1084 */
1085static errno_t async_manager_worker(void)
1086{
1087 ipc_call_t call;
1088 errno_t rc;
1089
1090 while (true) {
1091 rc = fibril_ipc_wait(&call, NULL);
1092 if (rc == EOK)
1093 handle_call(&call);
1094 }
1095
1096 return 0;
1097}
1098
1099/** Function to start async_manager as a standalone fibril.
1100 *
1101 * When more kernel threads are used, one async manager should exist per thread.
1102 *
1103 * @param arg Unused.
1104 * @return Never returns.
1105 *
1106 */
1107static errno_t async_manager_fibril(void *arg)
1108{
1109 async_manager_worker();
1110 return 0;
1111}
1112
1113/** Add one manager to manager list. */
1114fid_t async_create_manager(void)
1115{
1116 fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
1117 fibril_start(fid);
1118 return fid;
1119}
1120
1121/** Initialize the async framework.
1122 *
1123 */
1124void __async_server_init(void)
1125{
1126 if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
1127 abort();
1128
1129 if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
1130 abort();
1131
1132 if (!hash_table_create(&notification_hash_table, 0, 0,
1133 &notification_hash_table_ops))
1134 abort();
1135
1136 async_create_manager();
1137}
1138
1139errno_t async_answer_0(ipc_call_t *call, errno_t retval)
1140{
1141 return ipc_answer_0(call->cap_handle, retval);
1142}
1143
1144errno_t async_answer_1(ipc_call_t *call, errno_t retval, sysarg_t arg1)
1145{
1146 return ipc_answer_1(call->cap_handle, retval, arg1);
1147}
1148
1149errno_t async_answer_2(ipc_call_t *call, errno_t retval, sysarg_t arg1,
1150 sysarg_t arg2)
1151{
1152 return ipc_answer_2(call->cap_handle, retval, arg1, arg2);
1153}
1154
1155errno_t async_answer_3(ipc_call_t *call, errno_t retval, sysarg_t arg1,
1156 sysarg_t arg2, sysarg_t arg3)
1157{
1158 return ipc_answer_3(call->cap_handle, retval, arg1, arg2, arg3);
1159}
1160
1161errno_t async_answer_4(ipc_call_t *call, errno_t retval, sysarg_t arg1,
1162 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1163{
1164 return ipc_answer_4(call->cap_handle, retval, arg1, arg2, arg3, arg4);
1165}
1166
1167errno_t async_answer_5(ipc_call_t *call, errno_t retval, sysarg_t arg1,
1168 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
1169{
1170 return ipc_answer_5(call->cap_handle, retval, arg1, arg2, arg3, arg4,
1171 arg5);
1172}
1173
1174errno_t async_forward_fast(ipc_call_t *call, async_exch_t *exch,
1175 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
1176{
1177 assert(call);
1178
1179 if (exch == NULL)
1180 return ENOENT;
1181
1182 return ipc_forward_fast(call->cap_handle, exch->phone, imethod, arg1,
1183 arg2, mode);
1184}
1185
1186errno_t async_forward_slow(ipc_call_t *call, async_exch_t *exch,
1187 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
1188 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
1189{
1190 assert(call);
1191
1192 if (exch == NULL)
1193 return ENOENT;
1194
1195 return ipc_forward_slow(call->cap_handle, exch->phone, imethod, arg1,
1196 arg2, arg3, arg4, arg5, mode);
1197}
1198
1199/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
1200 *
1201 * Ask through phone for a new connection to some service.
1202 *
1203 * @param exch Exchange for sending the message.
1204 * @param iface Callback interface.
1205 * @param arg2 User defined argument.
1206 * @param arg3 User defined argument.
1207 *
1208 * @return Zero on success or an error code.
1209 *
1210 */
1211errno_t async_connect_to_me(async_exch_t *exch, iface_t iface, sysarg_t arg2,
1212 sysarg_t arg3)
1213{
1214 if (exch == NULL)
1215 return ENOENT;
1216
1217 ipc_call_t answer;
1218 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg2, arg3,
1219 &answer);
1220
1221 errno_t rc;
1222 async_wait_for(req, &rc);
1223 if (rc != EOK)
1224 return (errno_t) rc;
1225
1226 return EOK;
1227}
1228
1229/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
1230 *
1231 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN
1232 * calls so that the user doesn't have to remember the meaning of each IPC
1233 * argument.
1234 *
1235 * So far, this wrapper is to be used from within a connection fibril.
1236 *
1237 * @param call Storage for the data of the IPC_M_SHARE_IN call.
1238 * @param size Destination address space area size.
1239 *
1240 * @return True on success, false on failure.
1241 *
1242 */
1243bool async_share_in_receive(ipc_call_t *call, size_t *size)
1244{
1245 assert(call);
1246 assert(size);
1247
1248 async_get_call(call);
1249
1250 if (IPC_GET_IMETHOD(*call) != IPC_M_SHARE_IN)
1251 return false;
1252
1253 *size = (size_t) IPC_GET_ARG1(*call);
1254 return true;
1255}
1256
1257/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
1258 *
1259 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
1260 * calls so that the user doesn't have to remember the meaning of each IPC
1261 * argument.
1262 *
1263 * @param call IPC_M_DATA_READ call to answer.
1264 * @param src Source address space base.
1265 * @param flags Flags to be used for sharing. Bits can be only cleared.
1266 *
1267 * @return Zero on success or a value from @ref errno.h on failure.
1268 *
1269 */
1270errno_t async_share_in_finalize(ipc_call_t *call, void *src, unsigned int flags)
1271{
1272 assert(call);
1273
1274 // FIXME: The source has no business deciding destination address.
1275 return ipc_answer_3(call->cap_handle, EOK, (sysarg_t) src, (sysarg_t) flags,
1276 (sysarg_t) __progsymbols.end);
1277}
1278
1279/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
1280 *
1281 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT
1282 * calls so that the user doesn't have to remember the meaning of each IPC
1283 * argument.
1284 *
1285 * So far, this wrapper is to be used from within a connection fibril.
1286 *
1287 * @param call Storage for the data of the IPC_M_SHARE_OUT call.
1288 * @param size Storage for the source address space area size.
1289 * @param flags Storage for the sharing flags.
1290 *
1291 * @return True on success, false on failure.
1292 *
1293 */
1294bool async_share_out_receive(ipc_call_t *call, size_t *size,
1295 unsigned int *flags)
1296{
1297 assert(call);
1298 assert(size);
1299 assert(flags);
1300
1301 async_get_call(call);
1302
1303 if (IPC_GET_IMETHOD(*call) != IPC_M_SHARE_OUT)
1304 return false;
1305
1306 *size = (size_t) IPC_GET_ARG2(*call);
1307 *flags = (unsigned int) IPC_GET_ARG3(*call);
1308 return true;
1309}
1310
1311/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
1312 *
1313 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
1314 * calls so that the user doesn't have to remember the meaning of each IPC
1315 * argument.
1316 *
1317 * @param call IPC_M_DATA_WRITE call to answer.
1318 * @param dst Address of the storage for the destination address space area
1319 * base address.
1320 *
1321 * @return Zero on success or a value from @ref errno.h on failure.
1322 *
1323 */
1324errno_t async_share_out_finalize(ipc_call_t *call, void **dst)
1325{
1326 assert(call);
1327
1328 return ipc_answer_2(call->cap_handle, EOK, (sysarg_t) __progsymbols.end,
1329 (sysarg_t) dst);
1330}
1331
1332/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1333 *
1334 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
1335 * calls so that the user doesn't have to remember the meaning of each IPC
1336 * argument.
1337 *
1338 * So far, this wrapper is to be used from within a connection fibril.
1339 *
1340 * @param call Storage for the data of the IPC_M_DATA_READ.
1341 * @param size Storage for the maximum size. Can be NULL.
1342 *
1343 * @return True on success, false on failure.
1344 *
1345 */
1346bool async_data_read_receive(ipc_call_t *call, size_t *size)
1347{
1348 assert(call);
1349
1350 async_get_call(call);
1351
1352 if (IPC_GET_IMETHOD(*call) != IPC_M_DATA_READ)
1353 return false;
1354
1355 if (size)
1356 *size = (size_t) IPC_GET_ARG2(*call);
1357
1358 return true;
1359}
1360
1361/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
1362 *
1363 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
1364 * calls so that the user doesn't have to remember the meaning of each IPC
1365 * argument.
1366 *
1367 * @param call IPC_M_DATA_READ call to answer.
1368 * @param src Source address for the IPC_M_DATA_READ call.
1369 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
1370 * the maximum size announced by the sender.
1371 *
1372 * @return Zero on success or a value from @ref errno.h on failure.
1373 *
1374 */
1375errno_t async_data_read_finalize(ipc_call_t *call, const void *src, size_t size)
1376{
1377 assert(call);
1378
1379 return ipc_answer_2(call->cap_handle, EOK, (sysarg_t) src,
1380 (sysarg_t) size);
1381}
1382
1383/** Wrapper for forwarding any read request
1384 *
1385 */
1386errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
1387 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1388 ipc_call_t *dataptr)
1389{
1390 if (exch == NULL)
1391 return ENOENT;
1392
1393 ipc_call_t call;
1394 if (!async_data_read_receive(&call, NULL)) {
1395 async_answer_0(&call, EINVAL);
1396 return EINVAL;
1397 }
1398
1399 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
1400 dataptr);
1401 if (msg == 0) {
1402 async_answer_0(&call, EINVAL);
1403 return EINVAL;
1404 }
1405
1406 errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
1407 IPC_FF_ROUTE_FROM_ME);
1408 if (retval != EOK) {
1409 async_forget(msg);
1410 async_answer_0(&call, retval);
1411 return retval;
1412 }
1413
1414 errno_t rc;
1415 async_wait_for(msg, &rc);
1416
1417 return (errno_t) rc;
1418}
1419
1420/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1421 *
1422 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
1423 * calls so that the user doesn't have to remember the meaning of each IPC
1424 * argument.
1425 *
1426 * So far, this wrapper is to be used from within a connection fibril.
1427 *
1428 * @param call Storage for the data of the IPC_M_DATA_WRITE.
1429 * @param size Storage for the suggested size. May be NULL.
1430 *
1431 * @return True on success, false on failure.
1432 *
1433 */
1434bool async_data_write_receive(ipc_call_t *call, size_t *size)
1435{
1436 assert(call);
1437
1438 async_get_call(call);
1439
1440 if (IPC_GET_IMETHOD(*call) != IPC_M_DATA_WRITE)
1441 return false;
1442
1443 if (size)
1444 *size = (size_t) IPC_GET_ARG2(*call);
1445
1446 return true;
1447}
1448
1449/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
1450 *
1451 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
1452 * calls so that the user doesn't have to remember the meaning of each IPC
1453 * argument.
1454 *
1455 * @param call IPC_M_DATA_WRITE call to answer.
1456 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
1457 * @param size Final size for the IPC_M_DATA_WRITE call.
1458 *
1459 * @return Zero on success or a value from @ref errno.h on failure.
1460 *
1461 */
1462errno_t async_data_write_finalize(ipc_call_t *call, void *dst, size_t size)
1463{
1464 assert(call);
1465
1466 return async_answer_2(call, EOK, (sysarg_t) dst, (sysarg_t) size);
1467}
1468
1469/** Wrapper for receiving binary data or strings
1470 *
1471 * This wrapper only makes it more comfortable to use async_data_write_*
1472 * functions to receive binary data or strings.
1473 *
1474 * @param data Pointer to data pointer (which should be later disposed
1475 * by free()). If the operation fails, the pointer is not
1476 * touched.
1477 * @param nullterm If true then the received data is always zero terminated.
1478 * This also causes to allocate one extra byte beyond the
1479 * raw transmitted data.
1480 * @param min_size Minimum size (in bytes) of the data to receive.
1481 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
1482 * no limit.
1483 * @param granulariy If non-zero then the size of the received data has to
1484 * be divisible by this value.
1485 * @param received If not NULL, the size of the received data is stored here.
1486 *
1487 * @return Zero on success or a value from @ref errno.h on failure.
1488 *
1489 */
1490errno_t async_data_write_accept(void **data, const bool nullterm,
1491 const size_t min_size, const size_t max_size, const size_t granularity,
1492 size_t *received)
1493{
1494 assert(data);
1495
1496 ipc_call_t call;
1497 size_t size;
1498 if (!async_data_write_receive(&call, &size)) {
1499 async_answer_0(&call, EINVAL);
1500 return EINVAL;
1501 }
1502
1503 if (size < min_size) {
1504 async_answer_0(&call, EINVAL);
1505 return EINVAL;
1506 }
1507
1508 if ((max_size > 0) && (size > max_size)) {
1509 async_answer_0(&call, EINVAL);
1510 return EINVAL;
1511 }
1512
1513 if ((granularity > 0) && ((size % granularity) != 0)) {
1514 async_answer_0(&call, EINVAL);
1515 return EINVAL;
1516 }
1517
1518 void *arg_data;
1519
1520 if (nullterm)
1521 arg_data = malloc(size + 1);
1522 else
1523 arg_data = malloc(size);
1524
1525 if (arg_data == NULL) {
1526 async_answer_0(&call, ENOMEM);
1527 return ENOMEM;
1528 }
1529
1530 errno_t rc = async_data_write_finalize(&call, arg_data, size);
1531 if (rc != EOK) {
1532 free(arg_data);
1533 return rc;
1534 }
1535
1536 if (nullterm)
1537 ((char *) arg_data)[size] = 0;
1538
1539 *data = arg_data;
1540 if (received != NULL)
1541 *received = size;
1542
1543 return EOK;
1544}
1545
1546/** Wrapper for voiding any data that is about to be received
1547 *
1548 * This wrapper can be used to void any pending data
1549 *
1550 * @param retval Error value from @ref errno.h to be returned to the caller.
1551 *
1552 */
1553void async_data_write_void(errno_t retval)
1554{
1555 ipc_call_t call;
1556 async_data_write_receive(&call, NULL);
1557 async_answer_0(&call, retval);
1558}
1559
1560/** Wrapper for forwarding any data that is about to be received
1561 *
1562 */
1563errno_t async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
1564 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1565 ipc_call_t *dataptr)
1566{
1567 if (exch == NULL)
1568 return ENOENT;
1569
1570 ipc_call_t call;
1571 if (!async_data_write_receive(&call, NULL)) {
1572 async_answer_0(&call, EINVAL);
1573 return EINVAL;
1574 }
1575
1576 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
1577 dataptr);
1578 if (msg == 0) {
1579 async_answer_0(&call, EINVAL);
1580 return EINVAL;
1581 }
1582
1583 errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
1584 IPC_FF_ROUTE_FROM_ME);
1585 if (retval != EOK) {
1586 async_forget(msg);
1587 async_answer_0(&call, retval);
1588 return retval;
1589 }
1590
1591 errno_t rc;
1592 async_wait_for(msg, &rc);
1593
1594 return (errno_t) rc;
1595}
1596
1597/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1598 *
1599 * If the current call is IPC_M_CONNECT_TO_ME then a new
1600 * async session is created for the accepted phone.
1601 *
1602 * @param mgmt Exchange management style.
1603 *
1604 * @return New async session.
1605 * @return NULL on failure.
1606 *
1607 */
1608async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
1609{
1610 /* Accept the phone */
1611 ipc_call_t call;
1612 async_get_call(&call);
1613
1614 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(call);
1615
1616 if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) ||
1617 !CAP_HANDLE_VALID((phandle))) {
1618 async_answer_0(&call, EINVAL);
1619 return NULL;
1620 }
1621
1622 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1623 if (sess == NULL) {
1624 async_answer_0(&call, ENOMEM);
1625 return NULL;
1626 }
1627
1628 sess->iface = 0;
1629 sess->mgmt = mgmt;
1630 sess->phone = phandle;
1631 sess->arg1 = 0;
1632 sess->arg2 = 0;
1633 sess->arg3 = 0;
1634
1635 fibril_mutex_initialize(&sess->remote_state_mtx);
1636 sess->remote_state_data = NULL;
1637
1638 list_initialize(&sess->exch_list);
1639 fibril_mutex_initialize(&sess->mutex);
1640 atomic_set(&sess->refcnt, 0);
1641
1642 /* Acknowledge the connected phone */
1643 async_answer_0(&call, EOK);
1644
1645 return sess;
1646}
1647
1648/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1649 *
1650 * If the call is IPC_M_CONNECT_TO_ME then a new
1651 * async session is created. However, the phone is
1652 * not accepted automatically.
1653 *
1654 * @param mgmt Exchange management style.
1655 * @param call Call data.
1656 *
1657 * @return New async session.
1658 * @return NULL on failure.
1659 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
1660 *
1661 */
1662async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
1663{
1664 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(*call);
1665
1666 if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) ||
1667 !CAP_HANDLE_VALID((phandle)))
1668 return NULL;
1669
1670 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1671 if (sess == NULL)
1672 return NULL;
1673
1674 sess->iface = 0;
1675 sess->mgmt = mgmt;
1676 sess->phone = phandle;
1677 sess->arg1 = 0;
1678 sess->arg2 = 0;
1679 sess->arg3 = 0;
1680
1681 fibril_mutex_initialize(&sess->remote_state_mtx);
1682 sess->remote_state_data = NULL;
1683
1684 list_initialize(&sess->exch_list);
1685 fibril_mutex_initialize(&sess->mutex);
1686 atomic_set(&sess->refcnt, 0);
1687
1688 return sess;
1689}
1690
1691bool async_state_change_receive(ipc_call_t *call)
1692{
1693 assert(call);
1694
1695 async_get_call(call);
1696
1697 if (IPC_GET_IMETHOD(*call) != IPC_M_STATE_CHANGE_AUTHORIZE)
1698 return false;
1699
1700 return true;
1701}
1702
1703errno_t async_state_change_finalize(ipc_call_t *call, async_exch_t *other_exch)
1704{
1705 assert(call);
1706
1707 return async_answer_1(call, EOK, CAP_HANDLE_RAW(other_exch->phone));
1708}
1709
1710__noreturn void async_manager(void)
1711{
1712 fibril_event_t ever = FIBRIL_EVENT_INIT;
1713 fibril_wait_for(&ever);
1714 __builtin_unreachable();
1715}
1716
1717/** @}
1718 */
Note: See TracBrowser for help on using the repository browser.