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

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

Simplify the interaction between async_futex and fibril_switch().

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