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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 061274f was 061274f, checked in by Martin Decky <martin@…>, 7 years ago

simplify async framework internals

Do not duplicitly store or pass the cap_call_handle_t if we have the
ipc_call_t structure around which already contains the call capability
handle.

  • Property mode set to 100644
File size: 46.8 KB
Line 
1/*
2 * Copyright (c) 2006 Ondrej Palkovsky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/** @file
33 */
34
35/**
36 * Asynchronous library
37 *
38 * The aim of this library is to provide a facility for writing programs which
39 * utilize the asynchronous nature of HelenOS IPC, yet using a normal way of
40 * programming.
41 *
42 * You should be able to write very simple multithreaded programs. The async
43 * framework will automatically take care of most of the synchronization
44 * problems.
45 *
46 * Example of use (pseudo C):
47 *
48 * 1) Multithreaded client application
49 *
50 * fibril_create(fibril1, ...);
51 * fibril_create(fibril2, ...);
52 * ...
53 *
54 * int fibril1(void *arg)
55 * {
56 * conn = async_connect_me_to(...);
57 *
58 * exch = async_exchange_begin(conn);
59 * c1 = async_send(exch);
60 * async_exchange_end(exch);
61 *
62 * exch = async_exchange_begin(conn);
63 * c2 = async_send(exch);
64 * async_exchange_end(exch);
65 *
66 * async_wait_for(c1);
67 * async_wait_for(c2);
68 * ...
69 * }
70 *
71 *
72 * 2) Multithreaded server application
73 *
74 * main()
75 * {
76 * async_manager();
77 * }
78 *
79 * port_handler(ichandle, *icall)
80 * {
81 * if (want_refuse) {
82 * async_answer_0(ichandle, ELIMIT);
83 * return;
84 * }
85 * async_answer_0(ichandle, EOK);
86 *
87 * chandle = async_get_call(&call);
88 * somehow_handle_the_call(chandle, call);
89 * async_answer_2(chandle, 1, 2, 3);
90 *
91 * chandle = async_get_call(&call);
92 * ...
93 * }
94 *
95 */
96
97#define LIBC_ASYNC_C_
98#include <ipc/ipc.h>
99#include <async.h>
100#include "../private/async.h"
101#undef LIBC_ASYNC_C_
102
103#include <ipc/irq.h>
104#include <ipc/event.h>
105#include <futex.h>
106#include <fibril.h>
107#include <adt/hash_table.h>
108#include <adt/hash.h>
109#include <adt/list.h>
110#include <assert.h>
111#include <errno.h>
112#include <sys/time.h>
113#include <libarch/barrier.h>
114#include <stdbool.h>
115#include <stdlib.h>
116#include <mem.h>
117#include <stdlib.h>
118#include <macros.h>
119#include <as.h>
120#include <abi/mm/as.h>
121#include "../private/libc.h"
122#include "../private/fibril.h"
123
124#define DPRINTF(...) ((void) 0)
125
126/** Async framework global futex */
127futex_t async_futex = FUTEX_INITIALIZER;
128
129/** Number of threads waiting for IPC in the kernel. */
130static atomic_t threads_in_ipc_wait = { 0 };
131
132/** Call data */
133typedef struct {
134 link_t link;
135
136 ipc_call_t call;
137} msg_t;
138
139/* Client connection data */
140typedef struct {
141 ht_link_t link;
142
143 task_id_t in_task_id;
144 atomic_t refcnt;
145 void *data;
146} client_t;
147
148/* Server connection data */
149typedef struct {
150 awaiter_t wdata;
151
152 /** Hash table link. */
153 ht_link_t link;
154
155 /** Incoming client task ID. */
156 task_id_t in_task_id;
157
158 /** Incoming phone hash. */
159 sysarg_t in_phone_hash;
160
161 /** Link to the client tracking structure. */
162 client_t *client;
163
164 /** Messages that should be delivered to this fibril. */
165 list_t msg_queue;
166
167 /** Call data of the opening call. */
168 ipc_call_t call;
169
170 /** Identification of the closing call. */
171 cap_call_handle_t close_chandle;
172
173 /** Fibril function that will be used to handle the connection. */
174 async_port_handler_t handler;
175
176 /** Client data */
177 void *data;
178} connection_t;
179
180/* Member of notification_t::msg_list. */
181typedef struct {
182 link_t link;
183 ipc_call_t calldata;
184} notification_msg_t;
185
186/* Notification data */
187typedef struct {
188 /** notification_hash_table link */
189 ht_link_t htlink;
190
191 /** notification_queue link */
192 link_t qlink;
193
194 /** Notification method */
195 sysarg_t imethod;
196
197 /** Notification handler */
198 async_notification_handler_t handler;
199
200 /** Notification handler argument */
201 void *arg;
202
203 /** List of arrived notifications. */
204 list_t msg_list;
205} notification_t;
206
207/** Identifier of the incoming connection handled by the current fibril. */
208static fibril_local connection_t *fibril_connection;
209
210static void *default_client_data_constructor(void)
211{
212 return NULL;
213}
214
215static void default_client_data_destructor(void *data)
216{
217}
218
219static async_client_data_ctor_t async_client_data_create =
220 default_client_data_constructor;
221static async_client_data_dtor_t async_client_data_destroy =
222 default_client_data_destructor;
223
224void async_set_client_data_constructor(async_client_data_ctor_t ctor)
225{
226 assert(async_client_data_create == default_client_data_constructor);
227 async_client_data_create = ctor;
228}
229
230void async_set_client_data_destructor(async_client_data_dtor_t dtor)
231{
232 assert(async_client_data_destroy == default_client_data_destructor);
233 async_client_data_destroy = dtor;
234}
235
236static futex_t client_futex = FUTEX_INITIALIZER;
237static hash_table_t client_hash_table;
238
239// TODO: lockfree notification_queue?
240static futex_t notification_futex = FUTEX_INITIALIZER;
241static hash_table_t notification_hash_table;
242static LIST_INITIALIZE(notification_queue);
243static FIBRIL_SEMAPHORE_INITIALIZE(notification_semaphore, 0);
244
245static LIST_INITIALIZE(notification_freelist);
246static long notification_freelist_total = 0;
247static long notification_freelist_used = 0;
248
249static sysarg_t notification_avail = 0;
250
251/* The remaining structures are guarded by async_futex. */
252static hash_table_t conn_hash_table;
253static LIST_INITIALIZE(timeout_list);
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 futex_lock(&client_futex);
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 client = malloc(sizeof(client_t));
349 if (client) {
350 client->in_task_id = client_id;
351 client->data = async_client_data_create();
352
353 atomic_set(&client->refcnt, 1);
354 hash_table_insert(&client_hash_table, &client->link);
355 }
356 }
357
358 futex_unlock(&client_futex);
359 return client;
360}
361
362static void async_client_put(client_t *client)
363{
364 bool destroy;
365
366 futex_lock(&client_futex);
367
368 if (atomic_predec(&client->refcnt) == 0) {
369 hash_table_remove(&client_hash_table, &client->in_task_id);
370 destroy = true;
371 } else
372 destroy = false;
373
374 futex_unlock(&client_futex);
375
376 if (destroy) {
377 if (client->data)
378 async_client_data_destroy(client->data);
379
380 free(client);
381 }
382}
383
384/** Wrapper for client connection fibril.
385 *
386 * When a new connection arrives, a fibril with this implementing
387 * function is created.
388 *
389 * @param arg Connection structure pointer.
390 *
391 * @return Always zero.
392 *
393 */
394static errno_t connection_fibril(void *arg)
395{
396 assert(arg);
397
398 /*
399 * Setup fibril-local connection pointer.
400 */
401 fibril_connection = (connection_t *) arg;
402
403 /*
404 * Add our reference for the current connection in the client task
405 * tracking structure. If this is the first reference, create and
406 * hash in a new tracking structure.
407 */
408
409 client_t *client = async_client_get(fibril_connection->in_task_id, true);
410 if (!client) {
411 ipc_answer_0(fibril_connection->call.cap_handle, ENOMEM);
412 return 0;
413 }
414
415 fibril_connection->client = client;
416
417 /*
418 * Call the connection handler function.
419 */
420 fibril_connection->handler(fibril_connection->call.cap_handle,
421 &fibril_connection->call, fibril_connection->data);
422
423 /*
424 * Remove the reference for this client task connection.
425 */
426 async_client_put(client);
427
428 /*
429 * Remove myself from the connection hash table.
430 */
431 futex_lock(&async_futex);
432 hash_table_remove(&conn_hash_table, &(conn_key_t){
433 .task_id = fibril_connection->in_task_id,
434 .phone_hash = fibril_connection->in_phone_hash
435 });
436 futex_unlock(&async_futex);
437
438 /*
439 * Answer all remaining messages with EHANGUP.
440 */
441 while (!list_empty(&fibril_connection->msg_queue)) {
442 msg_t *msg =
443 list_get_instance(list_first(&fibril_connection->msg_queue),
444 msg_t, link);
445
446 list_remove(&msg->link);
447 ipc_answer_0(msg->call.cap_handle, EHANGUP);
448 free(msg);
449 }
450
451 /*
452 * If the connection was hung-up, answer the last call,
453 * i.e. IPC_M_PHONE_HUNGUP.
454 */
455 if (fibril_connection->close_chandle)
456 ipc_answer_0(fibril_connection->close_chandle, EOK);
457
458 free(fibril_connection);
459 return EOK;
460}
461
462/** Create a new fibril for a new connection.
463 *
464 * Create new fibril for connection, fill in connection structures and insert it
465 * into the hash table, so that later we can easily do routing of messages to
466 * particular fibrils.
467 *
468 * @param in_task_id Identification of the incoming connection.
469 * @param in_phone_hash Identification of the incoming connection.
470 * @param call Call data of the opening call. If call is NULL,
471 * the connection was opened by accepting the
472 * IPC_M_CONNECT_TO_ME call and this function is
473 * called directly by the server.
474 * @param handler Connection handler.
475 * @param data Client argument to pass to the connection handler.
476 *
477 * @return New fibril id or NULL on failure.
478 *
479 */
480static fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
481 ipc_call_t *call, async_port_handler_t handler, void *data)
482{
483 connection_t *conn = malloc(sizeof(*conn));
484 if (!conn) {
485 if (call)
486 ipc_answer_0(call->cap_handle, ENOMEM);
487
488 return (uintptr_t) NULL;
489 }
490
491 conn->in_task_id = in_task_id;
492 conn->in_phone_hash = in_phone_hash;
493 list_initialize(&conn->msg_queue);
494 conn->close_chandle = CAP_NIL;
495 conn->handler = handler;
496 conn->data = data;
497
498 if (call)
499 conn->call = *call;
500 else
501 conn->call.cap_handle = CAP_NIL;
502
503 /* We will activate the fibril ASAP */
504 conn->wdata.active = true;
505 conn->wdata.fid = fibril_create(connection_fibril, conn);
506
507 if (conn->wdata.fid == 0) {
508 free(conn);
509
510 if (call)
511 ipc_answer_0(call->cap_handle, ENOMEM);
512
513 return (uintptr_t) NULL;
514 }
515
516 /* Add connection to the connection hash table */
517
518 futex_lock(&async_futex);
519 hash_table_insert(&conn_hash_table, &conn->link);
520 futex_unlock(&async_futex);
521
522 fibril_add_ready(conn->wdata.fid);
523
524 return conn->wdata.fid;
525}
526
527/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
528 *
529 * Ask through phone for a new connection to some service.
530 *
531 * @param exch Exchange for sending the message.
532 * @param iface Callback interface.
533 * @param arg1 User defined argument.
534 * @param arg2 User defined argument.
535 * @param handler Callback handler.
536 * @param data Handler data.
537 * @param port_id ID of the newly created port.
538 *
539 * @return Zero on success or an error code.
540 *
541 */
542errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
543 sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
544{
545 if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
546 return EINVAL;
547
548 if (exch == NULL)
549 return ENOENT;
550
551 ipc_call_t answer;
552 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
553 &answer);
554
555 errno_t rc;
556 async_wait_for(req, &rc);
557 if (rc != EOK)
558 return rc;
559
560 rc = async_create_port_internal(iface, handler, data, port_id);
561 if (rc != EOK)
562 return rc;
563
564 sysarg_t phone_hash = IPC_GET_ARG5(answer);
565 fid_t fid = async_new_connection(answer.in_task_id, phone_hash,
566 NULL, handler, data);
567 if (fid == (uintptr_t) NULL)
568 return ENOMEM;
569
570 return EOK;
571}
572
573static size_t notification_key_hash(void *key)
574{
575 sysarg_t id = *(sysarg_t *) key;
576 return id;
577}
578
579static size_t notification_hash(const ht_link_t *item)
580{
581 notification_t *notification =
582 hash_table_get_inst(item, notification_t, htlink);
583 return notification_key_hash(&notification->imethod);
584}
585
586static bool notification_key_equal(void *key, const ht_link_t *item)
587{
588 sysarg_t id = *(sysarg_t *) key;
589 notification_t *notification =
590 hash_table_get_inst(item, notification_t, htlink);
591 return id == notification->imethod;
592}
593
594/** Operations for the notification hash table. */
595static hash_table_ops_t notification_hash_table_ops = {
596 .hash = notification_hash,
597 .key_hash = notification_key_hash,
598 .key_equal = notification_key_equal,
599 .equal = NULL,
600 .remove_callback = NULL
601};
602
603/** Sort in current fibril's timeout request.
604 *
605 * @param wd Wait data of the current fibril.
606 *
607 */
608void async_insert_timeout(awaiter_t *wd)
609{
610 assert(wd);
611
612 wd->to_event.occurred = false;
613 wd->to_event.inlist = true;
614
615 link_t *tmp = timeout_list.head.next;
616 while (tmp != &timeout_list.head) {
617 awaiter_t *cur =
618 list_get_instance(tmp, awaiter_t, to_event.link);
619
620 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
621 break;
622
623 tmp = tmp->next;
624 }
625
626 list_insert_before(&wd->to_event.link, tmp);
627}
628
629/** Try to route a call to an appropriate connection fibril.
630 *
631 * If the proper connection fibril is found, a message with the call is added to
632 * its message queue. If the fibril was not active, it is activated and all
633 * timeouts are unregistered.
634 *
635 * @param call Data of the incoming call.
636 *
637 * @return False if the call doesn't match any connection.
638 * @return True if the call was passed to the respective connection fibril.
639 *
640 */
641static bool route_call(ipc_call_t *call)
642{
643 assert(call);
644
645 futex_lock(&async_futex);
646
647 ht_link_t *link = hash_table_find(&conn_hash_table, &(conn_key_t){
648 .task_id = call->in_task_id,
649 .phone_hash = call->in_phone_hash
650 });
651 if (!link) {
652 futex_unlock(&async_futex);
653 return false;
654 }
655
656 connection_t *conn = hash_table_get_inst(link, connection_t, link);
657
658 msg_t *msg = malloc(sizeof(*msg));
659 if (!msg) {
660 futex_unlock(&async_futex);
661 return false;
662 }
663
664 msg->call = *call;
665 list_append(&msg->link, &conn->msg_queue);
666
667 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
668 conn->close_chandle = call->cap_handle;
669
670 /* If the connection fibril is waiting for an event, activate it */
671 if (!conn->wdata.active) {
672
673 /* If in timeout list, remove it */
674 if (conn->wdata.to_event.inlist) {
675 conn->wdata.to_event.inlist = false;
676 list_remove(&conn->wdata.to_event.link);
677 }
678
679 conn->wdata.active = true;
680 fibril_add_ready(conn->wdata.fid);
681 }
682
683 futex_unlock(&async_futex);
684 return true;
685}
686
687/** Function implementing the notification handler fibril. Never returns. */
688static errno_t notification_fibril_func(void *arg)
689{
690 (void) arg;
691
692 while (true) {
693 fibril_semaphore_down(&notification_semaphore);
694
695 futex_lock(&notification_futex);
696
697 /*
698 * The semaphore ensures that if we get this far,
699 * the queue must be non-empty.
700 */
701 assert(!list_empty(&notification_queue));
702
703 notification_t *notification = list_get_instance(
704 list_first(&notification_queue), notification_t, qlink);
705
706 async_notification_handler_t handler = notification->handler;
707 void *arg = notification->arg;
708
709 notification_msg_t *m = list_pop(&notification->msg_list,
710 notification_msg_t, link);
711 assert(m);
712 ipc_call_t calldata = m->calldata;
713
714 notification_freelist_used--;
715
716 if (notification_freelist_total > 64 &&
717 notification_freelist_total > 2 * notification_freelist_used) {
718 /* Going to free the structure if we have too much. */
719 notification_freelist_total--;
720 } else {
721 /* Otherwise add to freelist. */
722 list_append(&m->link, &notification_freelist);
723 m = NULL;
724 }
725
726 if (list_empty(&notification->msg_list))
727 list_remove(&notification->qlink);
728
729 futex_unlock(&notification_futex);
730
731 if (handler)
732 handler(&calldata, arg);
733
734 free(m);
735 }
736
737 /* Not reached. */
738 return EOK;
739}
740
741/**
742 * Creates a new dedicated fibril for handling notifications.
743 * By default, there is one such fibril. This function can be used to
744 * create more in order to increase the number of notification that can
745 * be processed concurrently.
746 *
747 * Currently, there is no way to destroy those fibrils after they are created.
748 */
749errno_t async_spawn_notification_handler(void)
750{
751 fid_t f = fibril_create(notification_fibril_func, NULL);
752 if (f == 0)
753 return ENOMEM;
754
755 fibril_add_ready(f);
756 return EOK;
757}
758
759/** Queue notification.
760 *
761 * @param call Data of the incoming call.
762 *
763 */
764static void queue_notification(ipc_call_t *call)
765{
766 assert(call);
767
768 futex_lock(&notification_futex);
769
770 notification_msg_t *m = list_pop(&notification_freelist,
771 notification_msg_t, link);
772
773 if (!m) {
774 futex_unlock(&notification_futex);
775 m = malloc(sizeof(notification_msg_t));
776 if (!m) {
777 DPRINTF("Out of memory.\n");
778 abort();
779 }
780
781 futex_lock(&notification_futex);
782 notification_freelist_total++;
783 }
784
785 ht_link_t *link = hash_table_find(&notification_hash_table,
786 &IPC_GET_IMETHOD(*call));
787 if (!link) {
788 /* Invalid notification. */
789 // TODO: Make sure this can't happen and turn it into assert.
790 notification_freelist_total--;
791 futex_unlock(&notification_futex);
792 free(m);
793 return;
794 }
795
796 notification_t *notification =
797 hash_table_get_inst(link, notification_t, htlink);
798
799 notification_freelist_used++;
800 m->calldata = *call;
801 list_append(&m->link, &notification->msg_list);
802
803 if (!link_in_use(&notification->qlink))
804 list_append(&notification->qlink, &notification_queue);
805
806 futex_unlock(&notification_futex);
807
808 fibril_semaphore_up(&notification_semaphore);
809}
810
811/**
812 * Creates a new notification structure and inserts it into the hash table.
813 *
814 * @param handler Function to call when notification is received.
815 * @param arg Argument for the handler function.
816 * @return The newly created notification structure.
817 */
818static notification_t *notification_create(async_notification_handler_t handler, void *arg)
819{
820 notification_t *notification = calloc(1, sizeof(notification_t));
821 if (!notification)
822 return NULL;
823
824 notification->handler = handler;
825 notification->arg = arg;
826
827 list_initialize(&notification->msg_list);
828
829 fid_t fib = 0;
830
831 futex_lock(&notification_futex);
832
833 if (notification_avail == 0) {
834 /* Attempt to create the first handler fibril. */
835 fib = fibril_create(notification_fibril_func, NULL);
836 if (fib == 0) {
837 futex_unlock(&notification_futex);
838 free(notification);
839 return NULL;
840 }
841 }
842
843 sysarg_t imethod = notification_avail;
844 notification_avail++;
845
846 notification->imethod = imethod;
847 hash_table_insert(&notification_hash_table, &notification->htlink);
848
849 futex_unlock(&notification_futex);
850
851 if (imethod == 0) {
852 assert(fib);
853 fibril_add_ready(fib);
854 }
855
856 return notification;
857}
858
859/** Subscribe to IRQ notification.
860 *
861 * @param inr IRQ number.
862 * @param handler Notification handler.
863 * @param data Notification handler client data.
864 * @param ucode Top-half pseudocode handler.
865 *
866 * @param[out] handle IRQ capability handle on success.
867 *
868 * @return An error code.
869 *
870 */
871errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
872 void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
873{
874 notification_t *notification = notification_create(handler, data);
875 if (!notification)
876 return ENOMEM;
877
878 cap_irq_handle_t ihandle;
879 errno_t rc = ipc_irq_subscribe(inr, notification->imethod, ucode,
880 &ihandle);
881 if (rc == EOK && handle != NULL) {
882 *handle = ihandle;
883 }
884 return rc;
885}
886
887/** Unsubscribe from IRQ notification.
888 *
889 * @param handle IRQ capability handle.
890 *
891 * @return Zero on success or an error code.
892 *
893 */
894errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
895{
896 // TODO: Remove entry from hash table
897 // to avoid memory leak
898
899 return ipc_irq_unsubscribe(ihandle);
900}
901
902/** Subscribe to event notifications.
903 *
904 * @param evno Event type to subscribe.
905 * @param handler Notification handler.
906 * @param data Notification handler client data.
907 *
908 * @return Zero on success or an error code.
909 *
910 */
911errno_t async_event_subscribe(event_type_t evno,
912 async_notification_handler_t handler, void *data)
913{
914 notification_t *notification = notification_create(handler, data);
915 if (!notification)
916 return ENOMEM;
917
918 return ipc_event_subscribe(evno, notification->imethod);
919}
920
921/** Subscribe to task event notifications.
922 *
923 * @param evno Event type to subscribe.
924 * @param handler Notification handler.
925 * @param data Notification handler client data.
926 *
927 * @return Zero on success or an error code.
928 *
929 */
930errno_t async_event_task_subscribe(event_task_type_t evno,
931 async_notification_handler_t handler, void *data)
932{
933 notification_t *notification = notification_create(handler, data);
934 if (!notification)
935 return ENOMEM;
936
937 return ipc_event_task_subscribe(evno, notification->imethod);
938}
939
940/** Unmask event notifications.
941 *
942 * @param evno Event type to unmask.
943 *
944 * @return Value returned by the kernel.
945 *
946 */
947errno_t async_event_unmask(event_type_t evno)
948{
949 return ipc_event_unmask(evno);
950}
951
952/** Unmask task event notifications.
953 *
954 * @param evno Event type to unmask.
955 *
956 * @return Value returned by the kernel.
957 *
958 */
959errno_t async_event_task_unmask(event_task_type_t evno)
960{
961 return ipc_event_task_unmask(evno);
962}
963
964/** Return new incoming message for the current (fibril-local) connection.
965 *
966 * @param call Storage where the incoming call data will be stored.
967 * @param usecs Timeout in microseconds. Zero denotes no timeout.
968 *
969 * @return If no timeout was specified, then a handle of the incoming call is
970 * returned. If a timeout is specified, then a handle of the incoming
971 * call is returned unless the timeout expires prior to receiving a
972 * message. In that case zero CAP_NIL is returned.
973 */
974cap_call_handle_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
975{
976 assert(call);
977 assert(fibril_connection);
978
979 /*
980 * Why doing this?
981 * GCC 4.1.0 coughs on fibril_connection-> dereference.
982 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
983 * I would never expect to find so many errors in
984 * a compiler.
985 */
986 connection_t *conn = fibril_connection;
987
988 futex_lock(&async_futex);
989
990 if (usecs) {
991 getuptime(&conn->wdata.to_event.expires);
992 tv_add_diff(&conn->wdata.to_event.expires, usecs);
993 } else
994 conn->wdata.to_event.inlist = false;
995
996 /* If nothing in queue, wait until something arrives */
997 while (list_empty(&conn->msg_queue)) {
998 if (conn->close_chandle) {
999 /*
1000 * Handle the case when the connection was already
1001 * closed by the client but the server did not notice
1002 * the first IPC_M_PHONE_HUNGUP call and continues to
1003 * call async_get_call_timeout(). Repeat
1004 * IPC_M_PHONE_HUNGUP until the caller notices.
1005 */
1006 memset(call, 0, sizeof(ipc_call_t));
1007 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
1008 futex_unlock(&async_futex);
1009 return conn->close_chandle;
1010 }
1011
1012 if (usecs)
1013 async_insert_timeout(&conn->wdata);
1014
1015 conn->wdata.active = false;
1016
1017 /*
1018 * Note: the current fibril will be rescheduled either due to a
1019 * timeout or due to an arriving message destined to it. In the
1020 * former case, handle_expired_timeouts() and, in the latter
1021 * case, route_call() will perform the wakeup.
1022 */
1023 fibril_switch(FIBRIL_FROM_BLOCKED);
1024
1025 if ((usecs) && (conn->wdata.to_event.occurred) &&
1026 (list_empty(&conn->msg_queue))) {
1027 /* If we timed out -> exit */
1028 futex_unlock(&async_futex);
1029 return CAP_NIL;
1030 }
1031 }
1032
1033 msg_t *msg = list_get_instance(list_first(&conn->msg_queue),
1034 msg_t, link);
1035 list_remove(&msg->link);
1036
1037 cap_call_handle_t chandle = msg->call.cap_handle;
1038 *call = msg->call;
1039 free(msg);
1040
1041 futex_unlock(&async_futex);
1042 return chandle;
1043}
1044
1045void *async_get_client_data(void)
1046{
1047 assert(fibril_connection);
1048 return fibril_connection->client->data;
1049}
1050
1051void *async_get_client_data_by_id(task_id_t client_id)
1052{
1053 client_t *client = async_client_get(client_id, false);
1054 if (!client)
1055 return NULL;
1056
1057 if (!client->data) {
1058 async_client_put(client);
1059 return NULL;
1060 }
1061
1062 return client->data;
1063}
1064
1065void async_put_client_data_by_id(task_id_t client_id)
1066{
1067 client_t *client = async_client_get(client_id, false);
1068
1069 assert(client);
1070 assert(client->data);
1071
1072 /* Drop the reference we got in async_get_client_data_by_hash(). */
1073 async_client_put(client);
1074
1075 /* Drop our own reference we got at the beginning of this function. */
1076 async_client_put(client);
1077}
1078
1079/** Handle a call that was received.
1080 *
1081 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
1082 * Otherwise the call is routed to its connection fibril.
1083 *
1084 * @param call Data of the incoming call.
1085 *
1086 */
1087static void handle_call(ipc_call_t *call)
1088{
1089 assert(call);
1090
1091 if (call->flags & IPC_CALL_ANSWERED)
1092 return;
1093
1094 if (call->cap_handle == CAP_NIL) {
1095 if (call->flags & IPC_CALL_NOTIF) {
1096 /* Kernel notification */
1097 queue_notification(call);
1098 }
1099 return;
1100 }
1101
1102 /* New connection */
1103 if (IPC_GET_IMETHOD(*call) == IPC_M_CONNECT_ME_TO) {
1104 iface_t iface = (iface_t) IPC_GET_ARG1(*call);
1105 sysarg_t in_phone_hash = IPC_GET_ARG5(*call);
1106
1107 // TODO: Currently ignores all ports but the first one.
1108 void *data;
1109 async_port_handler_t handler =
1110 async_get_port_handler(iface, 0, &data);
1111
1112 async_new_connection(call->in_task_id, in_phone_hash, call,
1113 handler, data);
1114 return;
1115 }
1116
1117 /* Try to route the call through the connection hash table */
1118 if (route_call(call))
1119 return;
1120
1121 /* Unknown call from unknown phone - hang it up */
1122 ipc_answer_0(call->cap_handle, EHANGUP);
1123}
1124
1125/** Fire all timeouts that expired. */
1126static suseconds_t handle_expired_timeouts(unsigned int *flags)
1127{
1128 /* Make sure the async_futex is held. */
1129 futex_assert_is_locked(&async_futex);
1130
1131 struct timeval tv;
1132 getuptime(&tv);
1133
1134 bool fired = false;
1135
1136 link_t *cur = list_first(&timeout_list);
1137 while (cur != NULL) {
1138 awaiter_t *waiter =
1139 list_get_instance(cur, awaiter_t, to_event.link);
1140
1141 if (tv_gt(&waiter->to_event.expires, &tv)) {
1142 if (fired) {
1143 *flags = SYNCH_FLAGS_NON_BLOCKING;
1144 return 0;
1145 }
1146 *flags = 0;
1147 return tv_sub_diff(&waiter->to_event.expires, &tv);
1148 }
1149
1150 list_remove(&waiter->to_event.link);
1151 waiter->to_event.inlist = false;
1152 waiter->to_event.occurred = true;
1153
1154 /*
1155 * Redundant condition?
1156 * The fibril should not be active when it gets here.
1157 */
1158 if (!waiter->active) {
1159 waiter->active = true;
1160 fibril_add_ready(waiter->fid);
1161 fired = true;
1162 }
1163
1164 cur = list_first(&timeout_list);
1165 }
1166
1167 if (fired) {
1168 *flags = SYNCH_FLAGS_NON_BLOCKING;
1169 return 0;
1170 }
1171
1172 return SYNCH_NO_TIMEOUT;
1173}
1174
1175/** Endless loop dispatching incoming calls and answers.
1176 *
1177 * @return Never returns.
1178 *
1179 */
1180static errno_t async_manager_worker(void)
1181{
1182 while (true) {
1183 futex_lock(&async_futex);
1184 fibril_switch(FIBRIL_FROM_MANAGER);
1185
1186 /*
1187 * The switch only returns when there is no non-manager fibril
1188 * it can run.
1189 */
1190
1191 unsigned int flags = SYNCH_FLAGS_NONE;
1192 suseconds_t next_timeout = handle_expired_timeouts(&flags);
1193 futex_unlock(&async_futex);
1194
1195 atomic_inc(&threads_in_ipc_wait);
1196
1197 ipc_call_t call;
1198 errno_t rc = ipc_wait_cycle(&call, next_timeout, flags);
1199
1200 atomic_dec(&threads_in_ipc_wait);
1201
1202 assert(rc == EOK);
1203 handle_call(&call);
1204 }
1205
1206 return 0;
1207}
1208
1209/** Function to start async_manager as a standalone fibril.
1210 *
1211 * When more kernel threads are used, one async manager should exist per thread.
1212 *
1213 * @param arg Unused.
1214 * @return Never returns.
1215 *
1216 */
1217static errno_t async_manager_fibril(void *arg)
1218{
1219 async_manager_worker();
1220 return 0;
1221}
1222
1223/** Add one manager to manager list. */
1224void async_create_manager(void)
1225{
1226 fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
1227 if (fid != 0)
1228 fibril_add_manager(fid);
1229}
1230
1231/** Remove one manager from manager list */
1232void async_destroy_manager(void)
1233{
1234 fibril_remove_manager();
1235}
1236
1237/** Initialize the async framework.
1238 *
1239 */
1240void __async_server_init(void)
1241{
1242 if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
1243 abort();
1244
1245 if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
1246 abort();
1247
1248 if (!hash_table_create(&notification_hash_table, 0, 0,
1249 &notification_hash_table_ops))
1250 abort();
1251}
1252
1253errno_t async_answer_0(cap_call_handle_t chandle, errno_t retval)
1254{
1255 return ipc_answer_0(chandle, retval);
1256}
1257
1258errno_t async_answer_1(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1)
1259{
1260 return ipc_answer_1(chandle, retval, arg1);
1261}
1262
1263errno_t async_answer_2(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
1264 sysarg_t arg2)
1265{
1266 return ipc_answer_2(chandle, retval, arg1, arg2);
1267}
1268
1269errno_t async_answer_3(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
1270 sysarg_t arg2, sysarg_t arg3)
1271{
1272 return ipc_answer_3(chandle, retval, arg1, arg2, arg3);
1273}
1274
1275errno_t async_answer_4(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
1276 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1277{
1278 return ipc_answer_4(chandle, retval, arg1, arg2, arg3, arg4);
1279}
1280
1281errno_t async_answer_5(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
1282 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
1283{
1284 return ipc_answer_5(chandle, retval, arg1, arg2, arg3, arg4, arg5);
1285}
1286
1287errno_t async_forward_fast(cap_call_handle_t chandle, async_exch_t *exch,
1288 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
1289{
1290 if (exch == NULL)
1291 return ENOENT;
1292
1293 return ipc_forward_fast(chandle, exch->phone, imethod, arg1, arg2, mode);
1294}
1295
1296errno_t async_forward_slow(cap_call_handle_t chandle, async_exch_t *exch,
1297 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
1298 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
1299{
1300 if (exch == NULL)
1301 return ENOENT;
1302
1303 return ipc_forward_slow(chandle, exch->phone, imethod, arg1, arg2, arg3,
1304 arg4, arg5, mode);
1305}
1306
1307/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
1308 *
1309 * Ask through phone for a new connection to some service.
1310 *
1311 * @param exch Exchange for sending the message.
1312 * @param arg1 User defined argument.
1313 * @param arg2 User defined argument.
1314 * @param arg3 User defined argument.
1315 *
1316 * @return Zero on success or an error code.
1317 *
1318 */
1319errno_t async_connect_to_me(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
1320 sysarg_t arg3)
1321{
1322 if (exch == NULL)
1323 return ENOENT;
1324
1325 ipc_call_t answer;
1326 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
1327 &answer);
1328
1329 errno_t rc;
1330 async_wait_for(req, &rc);
1331 if (rc != EOK)
1332 return (errno_t) rc;
1333
1334 return EOK;
1335}
1336
1337/** Interrupt one thread of this task from waiting for IPC. */
1338void async_poke(void)
1339{
1340 if (atomic_get(&threads_in_ipc_wait) > 0)
1341 ipc_poke();
1342}
1343
1344/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
1345 *
1346 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN
1347 * calls so that the user doesn't have to remember the meaning of each IPC
1348 * argument.
1349 *
1350 * So far, this wrapper is to be used from within a connection fibril.
1351 *
1352 * @param chandle Storage for the handle of the IPC_M_SHARE_IN call.
1353 * @param size Destination address space area size.
1354 *
1355 * @return True on success, false on failure.
1356 *
1357 */
1358bool async_share_in_receive(cap_call_handle_t *chandle, size_t *size)
1359{
1360 assert(chandle);
1361 assert(size);
1362
1363 ipc_call_t data;
1364 *chandle = async_get_call(&data);
1365
1366 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
1367 return false;
1368
1369 *size = (size_t) IPC_GET_ARG1(data);
1370 return true;
1371}
1372
1373/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
1374 *
1375 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
1376 * calls so that the user doesn't have to remember the meaning of each IPC
1377 * argument.
1378 *
1379 * @param chandle Handle of the IPC_M_DATA_READ call to answer.
1380 * @param src Source address space base.
1381 * @param flags Flags to be used for sharing. Bits can be only cleared.
1382 *
1383 * @return Zero on success or a value from @ref errno.h on failure.
1384 *
1385 */
1386errno_t async_share_in_finalize(cap_call_handle_t chandle, void *src,
1387 unsigned int flags)
1388{
1389 // FIXME: The source has no business deciding destination address.
1390 return ipc_answer_3(chandle, EOK, (sysarg_t) src, (sysarg_t) flags,
1391 (sysarg_t) _end);
1392}
1393
1394/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
1395 *
1396 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT
1397 * calls so that the user doesn't have to remember the meaning of each IPC
1398 * argument.
1399 *
1400 * So far, this wrapper is to be used from within a connection fibril.
1401 *
1402 * @param chandle Storage for the hash of the IPC_M_SHARE_OUT call.
1403 * @param size Storage for the source address space area size.
1404 * @param flags Storage for the sharing flags.
1405 *
1406 * @return True on success, false on failure.
1407 *
1408 */
1409bool async_share_out_receive(cap_call_handle_t *chandle, size_t *size,
1410 unsigned int *flags)
1411{
1412 assert(chandle);
1413 assert(size);
1414 assert(flags);
1415
1416 ipc_call_t data;
1417 *chandle = async_get_call(&data);
1418
1419 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
1420 return false;
1421
1422 *size = (size_t) IPC_GET_ARG2(data);
1423 *flags = (unsigned int) IPC_GET_ARG3(data);
1424 return true;
1425}
1426
1427/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
1428 *
1429 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
1430 * calls so that the user doesn't have to remember the meaning of each IPC
1431 * argument.
1432 *
1433 * @param chandle Handle of the IPC_M_DATA_WRITE call to answer.
1434 * @param dst Address of the storage for the destination address space area
1435 * base address.
1436 *
1437 * @return Zero on success or a value from @ref errno.h on failure.
1438 *
1439 */
1440errno_t async_share_out_finalize(cap_call_handle_t chandle, void **dst)
1441{
1442 return ipc_answer_2(chandle, EOK, (sysarg_t) _end, (sysarg_t) dst);
1443}
1444
1445/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1446 *
1447 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
1448 * calls so that the user doesn't have to remember the meaning of each IPC
1449 * argument.
1450 *
1451 * So far, this wrapper is to be used from within a connection fibril.
1452 *
1453 * @param chandle Storage for the handle of the IPC_M_DATA_READ.
1454 * @param size Storage for the maximum size. Can be NULL.
1455 *
1456 * @return True on success, false on failure.
1457 *
1458 */
1459bool async_data_read_receive(cap_call_handle_t *chandle, size_t *size)
1460{
1461 ipc_call_t data;
1462 return async_data_read_receive_call(chandle, &data, size);
1463}
1464
1465/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1466 *
1467 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
1468 * calls so that the user doesn't have to remember the meaning of each IPC
1469 * argument.
1470 *
1471 * So far, this wrapper is to be used from within a connection fibril.
1472 *
1473 * @param chandle Storage for the handle of the IPC_M_DATA_READ.
1474 * @param size Storage for the maximum size. Can be NULL.
1475 *
1476 * @return True on success, false on failure.
1477 *
1478 */
1479bool async_data_read_receive_call(cap_call_handle_t *chandle, ipc_call_t *data,
1480 size_t *size)
1481{
1482 assert(chandle);
1483 assert(data);
1484
1485 *chandle = async_get_call(data);
1486
1487 if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_READ)
1488 return false;
1489
1490 if (size)
1491 *size = (size_t) IPC_GET_ARG2(*data);
1492
1493 return true;
1494}
1495
1496/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
1497 *
1498 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
1499 * calls so that the user doesn't have to remember the meaning of each IPC
1500 * argument.
1501 *
1502 * @param chandle Handle of the IPC_M_DATA_READ call to answer.
1503 * @param src Source address for the IPC_M_DATA_READ call.
1504 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
1505 * the maximum size announced by the sender.
1506 *
1507 * @return Zero on success or a value from @ref errno.h on failure.
1508 *
1509 */
1510errno_t async_data_read_finalize(cap_call_handle_t chandle, const void *src,
1511 size_t size)
1512{
1513 return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) size);
1514}
1515
1516/** Wrapper for forwarding any read request
1517 *
1518 */
1519errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
1520 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1521 ipc_call_t *dataptr)
1522{
1523 if (exch == NULL)
1524 return ENOENT;
1525
1526 cap_call_handle_t chandle;
1527 if (!async_data_read_receive(&chandle, NULL)) {
1528 ipc_answer_0(chandle, EINVAL);
1529 return EINVAL;
1530 }
1531
1532 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
1533 dataptr);
1534 if (msg == 0) {
1535 ipc_answer_0(chandle, EINVAL);
1536 return EINVAL;
1537 }
1538
1539 errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
1540 IPC_FF_ROUTE_FROM_ME);
1541 if (retval != EOK) {
1542 async_forget(msg);
1543 ipc_answer_0(chandle, retval);
1544 return retval;
1545 }
1546
1547 errno_t rc;
1548 async_wait_for(msg, &rc);
1549
1550 return (errno_t) rc;
1551}
1552
1553/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1554 *
1555 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
1556 * calls so that the user doesn't have to remember the meaning of each IPC
1557 * argument.
1558 *
1559 * So far, this wrapper is to be used from within a connection fibril.
1560 *
1561 * @param chandle Storage for the handle of the IPC_M_DATA_WRITE.
1562 * @param size Storage for the suggested size. May be NULL.
1563 *
1564 * @return True on success, false on failure.
1565 *
1566 */
1567bool async_data_write_receive(cap_call_handle_t *chandle, size_t *size)
1568{
1569 ipc_call_t data;
1570 return async_data_write_receive_call(chandle, &data, size);
1571}
1572
1573/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1574 *
1575 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
1576 * calls so that the user doesn't have to remember the meaning of each IPC
1577 * argument.
1578 *
1579 * So far, this wrapper is to be used from within a connection fibril.
1580 *
1581 * @param chandle Storage for the handle of the IPC_M_DATA_WRITE.
1582 * @param data Storage for the ipc call data.
1583 * @param size Storage for the suggested size. May be NULL.
1584 *
1585 * @return True on success, false on failure.
1586 *
1587 */
1588bool async_data_write_receive_call(cap_call_handle_t *chandle, ipc_call_t *data,
1589 size_t *size)
1590{
1591 assert(chandle);
1592 assert(data);
1593
1594 *chandle = async_get_call(data);
1595
1596 if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_WRITE)
1597 return false;
1598
1599 if (size)
1600 *size = (size_t) IPC_GET_ARG2(*data);
1601
1602 return true;
1603}
1604
1605/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
1606 *
1607 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
1608 * calls so that the user doesn't have to remember the meaning of each IPC
1609 * argument.
1610 *
1611 * @param chandle Handle of the IPC_M_DATA_WRITE call to answer.
1612 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
1613 * @param size Final size for the IPC_M_DATA_WRITE call.
1614 *
1615 * @return Zero on success or a value from @ref errno.h on failure.
1616 *
1617 */
1618errno_t async_data_write_finalize(cap_call_handle_t chandle, void *dst,
1619 size_t size)
1620{
1621 return ipc_answer_2(chandle, EOK, (sysarg_t) dst, (sysarg_t) size);
1622}
1623
1624/** Wrapper for receiving binary data or strings
1625 *
1626 * This wrapper only makes it more comfortable to use async_data_write_*
1627 * functions to receive binary data or strings.
1628 *
1629 * @param data Pointer to data pointer (which should be later disposed
1630 * by free()). If the operation fails, the pointer is not
1631 * touched.
1632 * @param nullterm If true then the received data is always zero terminated.
1633 * This also causes to allocate one extra byte beyond the
1634 * raw transmitted data.
1635 * @param min_size Minimum size (in bytes) of the data to receive.
1636 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
1637 * no limit.
1638 * @param granulariy If non-zero then the size of the received data has to
1639 * be divisible by this value.
1640 * @param received If not NULL, the size of the received data is stored here.
1641 *
1642 * @return Zero on success or a value from @ref errno.h on failure.
1643 *
1644 */
1645errno_t async_data_write_accept(void **data, const bool nullterm,
1646 const size_t min_size, const size_t max_size, const size_t granularity,
1647 size_t *received)
1648{
1649 assert(data);
1650
1651 cap_call_handle_t chandle;
1652 size_t size;
1653 if (!async_data_write_receive(&chandle, &size)) {
1654 ipc_answer_0(chandle, EINVAL);
1655 return EINVAL;
1656 }
1657
1658 if (size < min_size) {
1659 ipc_answer_0(chandle, EINVAL);
1660 return EINVAL;
1661 }
1662
1663 if ((max_size > 0) && (size > max_size)) {
1664 ipc_answer_0(chandle, EINVAL);
1665 return EINVAL;
1666 }
1667
1668 if ((granularity > 0) && ((size % granularity) != 0)) {
1669 ipc_answer_0(chandle, EINVAL);
1670 return EINVAL;
1671 }
1672
1673 void *arg_data;
1674
1675 if (nullterm)
1676 arg_data = malloc(size + 1);
1677 else
1678 arg_data = malloc(size);
1679
1680 if (arg_data == NULL) {
1681 ipc_answer_0(chandle, ENOMEM);
1682 return ENOMEM;
1683 }
1684
1685 errno_t rc = async_data_write_finalize(chandle, arg_data, size);
1686 if (rc != EOK) {
1687 free(arg_data);
1688 return rc;
1689 }
1690
1691 if (nullterm)
1692 ((char *) arg_data)[size] = 0;
1693
1694 *data = arg_data;
1695 if (received != NULL)
1696 *received = size;
1697
1698 return EOK;
1699}
1700
1701/** Wrapper for voiding any data that is about to be received
1702 *
1703 * This wrapper can be used to void any pending data
1704 *
1705 * @param retval Error value from @ref errno.h to be returned to the caller.
1706 *
1707 */
1708void async_data_write_void(errno_t retval)
1709{
1710 cap_call_handle_t chandle;
1711 async_data_write_receive(&chandle, NULL);
1712 ipc_answer_0(chandle, retval);
1713}
1714
1715/** Wrapper for forwarding any data that is about to be received
1716 *
1717 */
1718errno_t async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
1719 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1720 ipc_call_t *dataptr)
1721{
1722 if (exch == NULL)
1723 return ENOENT;
1724
1725 cap_call_handle_t chandle;
1726 if (!async_data_write_receive(&chandle, NULL)) {
1727 ipc_answer_0(chandle, EINVAL);
1728 return EINVAL;
1729 }
1730
1731 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
1732 dataptr);
1733 if (msg == 0) {
1734 ipc_answer_0(chandle, EINVAL);
1735 return EINVAL;
1736 }
1737
1738 errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
1739 IPC_FF_ROUTE_FROM_ME);
1740 if (retval != EOK) {
1741 async_forget(msg);
1742 ipc_answer_0(chandle, retval);
1743 return retval;
1744 }
1745
1746 errno_t rc;
1747 async_wait_for(msg, &rc);
1748
1749 return (errno_t) rc;
1750}
1751
1752/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1753 *
1754 * If the current call is IPC_M_CONNECT_TO_ME then a new
1755 * async session is created for the accepted phone.
1756 *
1757 * @param mgmt Exchange management style.
1758 *
1759 * @return New async session.
1760 * @return NULL on failure.
1761 *
1762 */
1763async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
1764{
1765 /* Accept the phone */
1766 ipc_call_t call;
1767 cap_call_handle_t chandle = async_get_call(&call);
1768 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(call);
1769
1770 if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) ||
1771 !CAP_HANDLE_VALID((phandle))) {
1772 async_answer_0(chandle, EINVAL);
1773 return NULL;
1774 }
1775
1776 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1777 if (sess == NULL) {
1778 async_answer_0(chandle, ENOMEM);
1779 return NULL;
1780 }
1781
1782 sess->iface = 0;
1783 sess->mgmt = mgmt;
1784 sess->phone = phandle;
1785 sess->arg1 = 0;
1786 sess->arg2 = 0;
1787 sess->arg3 = 0;
1788
1789 fibril_mutex_initialize(&sess->remote_state_mtx);
1790 sess->remote_state_data = NULL;
1791
1792 list_initialize(&sess->exch_list);
1793 fibril_mutex_initialize(&sess->mutex);
1794 atomic_set(&sess->refcnt, 0);
1795
1796 /* Acknowledge the connected phone */
1797 async_answer_0(chandle, EOK);
1798
1799 return sess;
1800}
1801
1802/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1803 *
1804 * If the call is IPC_M_CONNECT_TO_ME then a new
1805 * async session is created. However, the phone is
1806 * not accepted automatically.
1807 *
1808 * @param mgmt Exchange management style.
1809 * @param call Call data.
1810 *
1811 * @return New async session.
1812 * @return NULL on failure.
1813 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
1814 *
1815 */
1816async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
1817{
1818 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(*call);
1819
1820 if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) ||
1821 !CAP_HANDLE_VALID((phandle)))
1822 return NULL;
1823
1824 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1825 if (sess == NULL)
1826 return NULL;
1827
1828 sess->iface = 0;
1829 sess->mgmt = mgmt;
1830 sess->phone = phandle;
1831 sess->arg1 = 0;
1832 sess->arg2 = 0;
1833 sess->arg3 = 0;
1834
1835 fibril_mutex_initialize(&sess->remote_state_mtx);
1836 sess->remote_state_data = NULL;
1837
1838 list_initialize(&sess->exch_list);
1839 fibril_mutex_initialize(&sess->mutex);
1840 atomic_set(&sess->refcnt, 0);
1841
1842 return sess;
1843}
1844
1845bool async_state_change_receive(cap_call_handle_t *chandle, sysarg_t *arg1,
1846 sysarg_t *arg2, sysarg_t *arg3)
1847{
1848 assert(chandle);
1849
1850 ipc_call_t call;
1851 *chandle = async_get_call(&call);
1852
1853 if (IPC_GET_IMETHOD(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
1854 return false;
1855
1856 if (arg1)
1857 *arg1 = IPC_GET_ARG1(call);
1858 if (arg2)
1859 *arg2 = IPC_GET_ARG2(call);
1860 if (arg3)
1861 *arg3 = IPC_GET_ARG3(call);
1862
1863 return true;
1864}
1865
1866errno_t async_state_change_finalize(cap_call_handle_t chandle,
1867 async_exch_t *other_exch)
1868{
1869 return ipc_answer_1(chandle, EOK, CAP_HANDLE_RAW(other_exch->phone));
1870}
1871
1872__noreturn void async_manager(void)
1873{
1874 futex_lock(&async_futex);
1875 fibril_switch(FIBRIL_FROM_DEAD);
1876 __builtin_unreachable();
1877}
1878
1879/** @}
1880 */
Note: See TracBrowser for help on using the repository browser.