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

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

Fibril/async implementation overhaul.

This commit marks the move towards treating the fibril library as a mere
implementation of a generic threading interface. Understood as a layer that
wraps the kernel threads, we not only have to wrap threading itself, but also
every syscall that blocks the kernel thread (by blocking, we mean thread not
doing useful work until an external event happens — e.g. locking a kernel
mutex or thread sleep is understood as blocking, but an as_area_create() is not,
despite potentially taking a long time to complete).

Consequently, we implement fibril_ipc_wait() as a fibril-native wrapper for
kernel's ipc_wait(), and also implement timer functionality like timeouts
as part of the fibril library. This removes the interdependency between fibril
implementation and the async framework — in theory, the fibril API could be
reimplemented as a simple 1:1 shim, and the async framework would continue
working normally (note that the current implementation of loader complicates
this).

To better isolate the fibril internals from the implementation of high-level
synchronization, a fibril_event_t is added. This object conceptually acts
like a single slot wait queue. All other synchronization is implemented in
terms of this primitive.

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