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

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

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

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