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

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

Put notifications into a dynamic queue instead of forgetting them.

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