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

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

Remove undefined references to main program from shared libc.

  • Property mode set to 100644
File size: 44.8 KB
RevLine 
[06502f7d]1/*
[df4ed85]2 * Copyright (c) 2006 Ondrej Palkovsky
[06502f7d]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.
[b2951e2]27 */
28
[a46da63]29/** @addtogroup libc
[b2951e2]30 * @{
31 */
32/** @file
[c07544d3]33 */
[06502f7d]34
[80649a91]35/**
36 * Asynchronous library
37 *
[c07544d3]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.
[80649a91]41 *
[79ae36dd]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.
[80649a91]45 *
[9591265]46 * Example of use (pseudo C):
[c07544d3]47 *
[80649a91]48 * 1) Multithreaded client application
[9591265]49 *
[c07544d3]50 * fibril_create(fibril1, ...);
51 * fibril_create(fibril2, ...);
52 * ...
53 *
54 * int fibril1(void *arg)
55 * {
[79ae36dd]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 *
[c07544d3]66 * async_wait_for(c1);
67 * async_wait_for(c2);
68 * ...
69 * }
[80649a91]70 *
71 *
72 * 2) Multithreaded server application
73 *
[c07544d3]74 * main()
75 * {
76 * async_manager();
77 * }
78 *
[984a9ba]79 * port_handler(ipc_call_t *icall)
[c07544d3]80 * {
81 * if (want_refuse) {
[984a9ba]82 * async_answer_0(icall, ELIMIT);
[c07544d3]83 * return;
84 * }
[80649a91]85 *
[984a9ba]86 * async_answer_0(icall, EOK);
[53ca318]87 *
[984a9ba]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);
[c07544d3]93 * ...
94 * }
[a2cd194]95 *
[80649a91]96 */
[9591265]97
[64d2b10]98#define LIBC_ASYNC_C_
99#include <ipc/ipc.h>
[80649a91]100#include <async.h>
[49a796f1]101#include "../private/async.h"
[64d2b10]102#undef LIBC_ASYNC_C_
103
[8820544]104#include <ipc/irq.h>
105#include <ipc/event.h>
[64d2b10]106#include <futex.h>
[bc1f1c2]107#include <fibril.h>
[d9c8c81]108#include <adt/hash_table.h>
[853802e]109#include <adt/hash.h>
[d9c8c81]110#include <adt/list.h>
[80649a91]111#include <assert.h>
112#include <errno.h>
[daa90e8]113#include <sys/time.h>
[c0699467]114#include <libarch/barrier.h>
[3e6a98c5]115#include <stdbool.h>
[38d150e]116#include <stdlib.h>
[79ae36dd]117#include <mem.h>
118#include <stdlib.h>
[e2ab36f1]119#include <macros.h>
[101516d]120#include <as.h>
[ae6021d]121#include <abi/mm/as.h>
[49a796f1]122#include "../private/libc.h"
[d73d992]123#include "../private/fibril.h"
[5da7199]124
[8dab988]125#define DPRINTF(...) ((void) 0)
126
[79ae36dd]127/** Async framework global futex */
[927a181e]128futex_t async_futex = FUTEX_INITIALIZER;
[80649a91]129
[8619f25]130/** Number of threads waiting for IPC in the kernel. */
[c8afd5a]131static atomic_t threads_in_ipc_wait = { 0 };
[8619f25]132
[79ae36dd]133/** Call data */
[80649a91]134typedef struct {
135 link_t link;
136 ipc_call_t call;
137} msg_t;
138
[79ae36dd]139/* Client connection data */
[c80fdd0]140typedef struct {
[062d900]141 ht_link_t link;
[a35b458]142
[649f087]143 task_id_t in_task_id;
[79ae36dd]144 atomic_t refcnt;
[c80fdd0]145 void *data;
146} client_t;
147
[79ae36dd]148/* Server connection data */
[80649a91]149typedef struct {
[49d072e]150 awaiter_t wdata;
[a35b458]151
[e70bfa5]152 /** Hash table link. */
[062d900]153 ht_link_t link;
[a35b458]154
[e2ab36f1]155 /** Incoming client task ID. */
156 task_id_t in_task_id;
[a35b458]157
[e70bfa5]158 /** Incoming phone hash. */
[96b02eb9]159 sysarg_t in_phone_hash;
[a35b458]160
[23882034]161 /** Link to the client tracking structure. */
162 client_t *client;
[a35b458]163
[e70bfa5]164 /** Messages that should be delivered to this fibril. */
[b72efe8]165 list_t msg_queue;
[a35b458]166
[e70bfa5]167 /** Call data of the opening call. */
[80649a91]168 ipc_call_t call;
[a35b458]169
[e70bfa5]170 /** Identification of the closing call. */
[eadaeae8]171 cap_call_handle_t close_chandle;
[a35b458]172
[e70bfa5]173 /** Fibril function that will be used to handle the connection. */
[b688fd8]174 async_port_handler_t handler;
[a35b458]175
[b688fd8]176 /** Client data */
177 void *data;
[80649a91]178} connection_t;
179
[8dab988]180/* Member of notification_t::msg_list. */
181typedef struct {
182 link_t link;
183 ipc_call_t calldata;
184} notification_msg_t;
185
[8820544]186/* Notification data */
187typedef struct {
[3b1cc8d]188 /** notification_hash_table link */
189 ht_link_t htlink;
190
191 /** notification_queue link */
192 link_t qlink;
[a35b458]193
[8820544]194 /** Notification method */
195 sysarg_t imethod;
[a35b458]196
[8820544]197 /** Notification handler */
198 async_notification_handler_t handler;
[a35b458]199
[3b1cc8d]200 /** Notification handler argument */
201 void *arg;
202
[8dab988]203 /** List of arrived notifications. */
204 list_t msg_list;
[8820544]205} notification_t;
206
[bc1f1c2]207/** Identifier of the incoming connection handled by the current fibril. */
[79ae36dd]208static fibril_local connection_t *fibril_connection;
[e70bfa5]209
[46eec3b]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{
[f302586]226 assert(async_client_data_create == default_client_data_constructor);
[46eec3b]227 async_client_data_create = ctor;
228}
229
230void async_set_client_data_destructor(async_client_data_dtor_t dtor)
231{
[f302586]232 assert(async_client_data_destroy == default_client_data_destructor);
[46eec3b]233 async_client_data_destroy = dtor;
234}
235
[3bd1d7d4]236static futex_t client_futex = FUTEX_INITIALIZER;
[c80fdd0]237static hash_table_t client_hash_table;
[3b1cc8d]238
239// TODO: lockfree notification_queue?
240static futex_t notification_futex = FUTEX_INITIALIZER;
[8820544]241static hash_table_t notification_hash_table;
[3b1cc8d]242static LIST_INITIALIZE(notification_queue);
243static FIBRIL_SEMAPHORE_INITIALIZE(notification_semaphore, 0);
244
[8dab988]245static LIST_INITIALIZE(notification_freelist);
246static long notification_freelist_total = 0;
247static long notification_freelist_used = 0;
248
[8820544]249static sysarg_t notification_avail = 0;
250
[3bd1d7d4]251/* The remaining structures are guarded by async_futex. */
252static hash_table_t conn_hash_table;
253static LIST_INITIALIZE(timeout_list);
254
[8820544]255static size_t client_key_hash(void *key)
[c80fdd0]256{
[8820544]257 task_id_t in_task_id = *(task_id_t *) key;
258 return in_task_id;
[c80fdd0]259}
260
[062d900]261static size_t client_hash(const ht_link_t *item)
[c80fdd0]262{
[062d900]263 client_t *client = hash_table_get_inst(item, client_t, link);
264 return client_key_hash(&client->in_task_id);
[c80fdd0]265}
266
[8820544]267static bool client_key_equal(void *key, const ht_link_t *item)
[c80fdd0]268{
[8820544]269 task_id_t in_task_id = *(task_id_t *) key;
[062d900]270 client_t *client = hash_table_get_inst(item, client_t, link);
[8820544]271 return in_task_id == client->in_task_id;
[c80fdd0]272}
273
274/** Operations for the client hash table. */
[062d900]275static hash_table_ops_t client_hash_table_ops = {
[c80fdd0]276 .hash = client_hash,
[062d900]277 .key_hash = client_key_hash,
278 .key_equal = client_key_equal,
[4e00f87]279 .equal = NULL,
280 .remove_callback = NULL
[c80fdd0]281};
[80649a91]282
[853802e]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
[e70bfa5]289 *
[853802e]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.
[c07544d3]296 *
297 * @return Index into the connection hash table.
[e70bfa5]298 *
299 */
[062d900]300static size_t conn_key_hash(void *key)
[450cd3a]301{
[853802e]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;
[450cd3a]309}
[06502f7d]310
[062d900]311static size_t conn_hash(const ht_link_t *item)
[450cd3a]312{
[062d900]313 connection_t *conn = hash_table_get_inst(item, connection_t, link);
[853802e]314 return conn_key_hash(&(conn_key_t){
315 .task_id = conn->in_task_id,
316 .phone_hash = conn->in_phone_hash
317 });
[450cd3a]318}
[06502f7d]319
[062d900]320static bool conn_key_equal(void *key, const ht_link_t *item)
[450cd3a]321{
[853802e]322 conn_key_t *ck = (conn_key_t *) key;
[062d900]323 connection_t *conn = hash_table_get_inst(item, connection_t, link);
[853802e]324 return ((ck->task_id == conn->in_task_id) &&
325 (ck->phone_hash == conn->in_phone_hash));
[450cd3a]326}
327
[e70bfa5]328/** Operations for the connection hash table. */
[062d900]329static hash_table_ops_t conn_hash_table_ops = {
[80649a91]330 .hash = conn_hash,
[062d900]331 .key_hash = conn_key_hash,
332 .key_equal = conn_key_equal,
[4e00f87]333 .equal = NULL,
334 .remove_callback = NULL
[80649a91]335};
336
[9ef495f]337static client_t *async_client_get(task_id_t client_id, bool create)
338{
339 client_t *client = NULL;
[a35b458]340
[3bd1d7d4]341 futex_lock(&client_futex);
[9ef495f]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) {
[3bd1d7d4]347 // TODO: move the malloc out of critical section
[9ef495f]348 client = malloc(sizeof(client_t));
349 if (client) {
350 client->in_task_id = client_id;
351 client->data = async_client_data_create();
[a35b458]352
[9ef495f]353 atomic_set(&client->refcnt, 1);
354 hash_table_insert(&client_hash_table, &client->link);
355 }
356 }
[a35b458]357
[3bd1d7d4]358 futex_unlock(&client_futex);
[9ef495f]359 return client;
360}
361
362static void async_client_put(client_t *client)
363{
364 bool destroy;
[a35b458]365
[3bd1d7d4]366 futex_lock(&client_futex);
[a35b458]367
[9ef495f]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;
[a35b458]373
[3bd1d7d4]374 futex_unlock(&client_futex);
[a35b458]375
[9ef495f]376 if (destroy) {
377 if (client->data)
378 async_client_data_destroy(client->data);
[a35b458]379
[9ef495f]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 */
[b7fd2a0]394static errno_t connection_fibril(void *arg)
[9ef495f]395{
396 assert(arg);
[a35b458]397
[9ef495f]398 /*
399 * Setup fibril-local connection pointer.
400 */
401 fibril_connection = (connection_t *) arg;
[a35b458]402
[9ef495f]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 */
[a35b458]408
[9ef495f]409 client_t *client = async_client_get(fibril_connection->in_task_id, true);
410 if (!client) {
[061274f]411 ipc_answer_0(fibril_connection->call.cap_handle, ENOMEM);
[9ef495f]412 return 0;
413 }
[a35b458]414
[9ef495f]415 fibril_connection->client = client;
[a35b458]416
[9ef495f]417 /*
418 * Call the connection handler function.
419 */
[984a9ba]420 fibril_connection->handler(&fibril_connection->call,
421 fibril_connection->data);
[a35b458]422
[9ef495f]423 /*
424 * Remove the reference for this client task connection.
425 */
426 async_client_put(client);
[a35b458]427
[9ef495f]428 /*
429 * Remove myself from the connection hash table.
430 */
[95838f1]431 futex_lock(&async_futex);
[853802e]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 });
[95838f1]436 futex_unlock(&async_futex);
[a35b458]437
[9ef495f]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);
[a35b458]445
[9ef495f]446 list_remove(&msg->link);
[061274f]447 ipc_answer_0(msg->call.cap_handle, EHANGUP);
[9ef495f]448 free(msg);
449 }
[a35b458]450
[9ef495f]451 /*
452 * If the connection was hung-up, answer the last call,
453 * i.e. IPC_M_PHONE_HUNGUP.
454 */
[01c3bb4]455 if (fibril_connection->close_chandle)
456 ipc_answer_0(fibril_connection->close_chandle, EOK);
[a35b458]457
[9ef495f]458 free(fibril_connection);
[d5c1051]459 return EOK;
[9ef495f]460}
461
462/** Create a new fibril for a new connection.
463 *
[01c3bb4]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.
[9ef495f]467 *
[01c3bb4]468 * @param in_task_id Identification of the incoming connection.
469 * @param in_phone_hash Identification of the incoming connection.
[061274f]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.
[01c3bb4]474 * @param handler Connection handler.
475 * @param data Client argument to pass to the connection handler.
[9ef495f]476 *
[01c3bb4]477 * @return New fibril id or NULL on failure.
[9ef495f]478 *
479 */
480static fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
[061274f]481 ipc_call_t *call, async_port_handler_t handler, void *data)
[9ef495f]482{
483 connection_t *conn = malloc(sizeof(*conn));
484 if (!conn) {
[061274f]485 if (call)
486 ipc_answer_0(call->cap_handle, ENOMEM);
[a35b458]487
[9ef495f]488 return (uintptr_t) NULL;
489 }
[a35b458]490
[9ef495f]491 conn->in_task_id = in_task_id;
492 conn->in_phone_hash = in_phone_hash;
493 list_initialize(&conn->msg_queue);
[01c3bb4]494 conn->close_chandle = CAP_NIL;
[9ef495f]495 conn->handler = handler;
496 conn->data = data;
[a35b458]497
[9ef495f]498 if (call)
499 conn->call = *call;
[061274f]500 else
501 conn->call.cap_handle = CAP_NIL;
[a35b458]502
[9ef495f]503 /* We will activate the fibril ASAP */
504 conn->wdata.active = true;
505 conn->wdata.fid = fibril_create(connection_fibril, conn);
[a35b458]506
[9ef495f]507 if (conn->wdata.fid == 0) {
508 free(conn);
[a35b458]509
[061274f]510 if (call)
511 ipc_answer_0(call->cap_handle, ENOMEM);
[a35b458]512
[9ef495f]513 return (uintptr_t) NULL;
514 }
[a35b458]515
[9ef495f]516 /* Add connection to the connection hash table */
[a35b458]517
[95838f1]518 futex_lock(&async_futex);
[9ef495f]519 hash_table_insert(&conn_hash_table, &conn->link);
[95838f1]520 futex_unlock(&async_futex);
[a35b458]521
[9ef495f]522 fibril_add_ready(conn->wdata.fid);
[a35b458]523
[9ef495f]524 return conn->wdata.fid;
525}
526
[78bb04b]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 *
[cde999a]539 * @return Zero on success or an error code.
[78bb04b]540 *
541 */
[b7fd2a0]542errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
[78bb04b]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;
[a35b458]547
[78bb04b]548 if (exch == NULL)
549 return ENOENT;
[a35b458]550
[78bb04b]551 ipc_call_t answer;
552 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
553 &answer);
[a35b458]554
[fda19b8]555 errno_t rc;
556 async_wait_for(req, &rc);
557 if (rc != EOK)
558 return rc;
[a35b458]559
[fda19b8]560 rc = async_create_port_internal(iface, handler, data, port_id);
561 if (rc != EOK)
562 return rc;
[a35b458]563
[fda19b8]564 sysarg_t phone_hash = IPC_GET_ARG5(answer);
[78bb04b]565 fid_t fid = async_new_connection(answer.in_task_id, phone_hash,
[061274f]566 NULL, handler, data);
[78bb04b]567 if (fid == (uintptr_t) NULL)
568 return ENOMEM;
[a35b458]569
[78bb04b]570 return EOK;
571}
572
[8820544]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 =
[3b1cc8d]582 hash_table_get_inst(item, notification_t, htlink);
[8820544]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 =
[3b1cc8d]590 hash_table_get_inst(item, notification_t, htlink);
[8820544]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
[e70bfa5]603/** Sort in current fibril's timeout request.
[49d072e]604 *
[c07544d3]605 * @param wd Wait data of the current fibril.
606 *
[49d072e]607 */
[b6ee5b1]608void async_insert_timeout(awaiter_t *wd)
[49d072e]609{
[79ae36dd]610 assert(wd);
[a35b458]611
[f53cc81]612 wd->to_event.occurred = false;
613 wd->to_event.inlist = true;
[a35b458]614
[b72efe8]615 link_t *tmp = timeout_list.head.next;
616 while (tmp != &timeout_list.head) {
[3bacee1]617 awaiter_t *cur =
618 list_get_instance(tmp, awaiter_t, to_event.link);
[a35b458]619
[f53cc81]620 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
[49d072e]621 break;
[a35b458]622
[49d072e]623 tmp = tmp->next;
624 }
[a35b458]625
[b72efe8]626 list_insert_before(&wd->to_event.link, tmp);
[49d072e]627}
628
[e70bfa5]629/** Try to route a call to an appropriate connection fibril.
[80649a91]630 *
[36c9234]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 *
[061274f]635 * @param call Data of the incoming call.
[c07544d3]636 *
637 * @return False if the call doesn't match any connection.
[47b7006]638 * @return True if the call was passed to the respective connection fibril.
[36c9234]639 *
[80649a91]640 */
[061274f]641static bool route_call(ipc_call_t *call)
[450cd3a]642{
[79ae36dd]643 assert(call);
[a35b458]644
[95838f1]645 futex_lock(&async_futex);
[a35b458]646
[853802e]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 });
[8820544]651 if (!link) {
[95838f1]652 futex_unlock(&async_futex);
[c07544d3]653 return false;
[450cd3a]654 }
[a35b458]655
[8820544]656 connection_t *conn = hash_table_get_inst(link, connection_t, link);
[a35b458]657
[c07544d3]658 msg_t *msg = malloc(sizeof(*msg));
659 if (!msg) {
[95838f1]660 futex_unlock(&async_futex);
[c07544d3]661 return false;
662 }
[a35b458]663
[80649a91]664 msg->call = *call;
665 list_append(&msg->link, &conn->msg_queue);
[a35b458]666
[228e490]667 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
[061274f]668 conn->close_chandle = call->cap_handle;
[a35b458]669
[36c9234]670 /* If the connection fibril is waiting for an event, activate it */
[49d072e]671 if (!conn->wdata.active) {
[a35b458]672
[49d072e]673 /* If in timeout list, remove it */
[f53cc81]674 if (conn->wdata.to_event.inlist) {
675 conn->wdata.to_event.inlist = false;
676 list_remove(&conn->wdata.to_event.link);
[49d072e]677 }
[a35b458]678
[c07544d3]679 conn->wdata.active = true;
[bc1f1c2]680 fibril_add_ready(conn->wdata.fid);
[80649a91]681 }
[a35b458]682
[95838f1]683 futex_unlock(&async_futex);
[c07544d3]684 return true;
685}
[80649a91]686
[3b1cc8d]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
[8dab988]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 }
[3b1cc8d]725
[8dab988]726 if (list_empty(&notification->msg_list))
727 list_remove(&notification->qlink);
[3b1cc8d]728
[8dab988]729 futex_unlock(&notification_futex);
[3b1cc8d]730
731 if (handler)
732 handler(&calldata, arg);
[8dab988]733
734 free(m);
[3b1cc8d]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.
[c07544d3]760 *
[c170438]761 * @param call Data of the incoming call.
[58563585]762 *
[c07544d3]763 */
[3b1cc8d]764static void queue_notification(ipc_call_t *call)
[c07544d3]765{
[c170438]766 assert(call);
[a35b458]767
[3b1cc8d]768 futex_lock(&notification_futex);
[a35b458]769
[8dab988]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
[8820544]785 ht_link_t *link = hash_table_find(&notification_hash_table,
[c170438]786 &IPC_GET_IMETHOD(*call));
[3b1cc8d]787 if (!link) {
788 /* Invalid notification. */
789 // TODO: Make sure this can't happen and turn it into assert.
[8dab988]790 notification_freelist_total--;
[3b1cc8d]791 futex_unlock(&notification_futex);
[8dab988]792 free(m);
[3b1cc8d]793 return;
[8820544]794 }
[a35b458]795
[3b1cc8d]796 notification_t *notification =
797 hash_table_get_inst(link, notification_t, htlink);
798
[8dab988]799 notification_freelist_used++;
800 m->calldata = *call;
801 list_append(&m->link, &notification->msg_list);
[3b1cc8d]802
[8dab988]803 if (!link_in_use(&notification->qlink))
804 list_append(&notification->qlink, &notification_queue);
[3b1cc8d]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
[8dab988]827 list_initialize(&notification->msg_list);
828
[3b1cc8d]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 }
[a35b458]855
[3b1cc8d]856 return notification;
[80649a91]857}
858
[8820544]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 *
[071a1ddb]866 * @param[out] handle IRQ capability handle on success.
867 *
[cde999a]868 * @return An error code.
[8820544]869 *
870 */
[b7fd2a0]871errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
[eadaeae8]872 void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
[8820544]873{
[3b1cc8d]874 notification_t *notification = notification_create(handler, data);
[8820544]875 if (!notification)
876 return ENOMEM;
[a35b458]877
[eadaeae8]878 cap_irq_handle_t ihandle;
[3b1cc8d]879 errno_t rc = ipc_irq_subscribe(inr, notification->imethod, ucode,
880 &ihandle);
[071a1ddb]881 if (rc == EOK && handle != NULL) {
[eadaeae8]882 *handle = ihandle;
[9233e9d]883 }
[071a1ddb]884 return rc;
[8820544]885}
886
887/** Unsubscribe from IRQ notification.
888 *
[eadaeae8]889 * @param handle IRQ capability handle.
[8820544]890 *
[cde999a]891 * @return Zero on success or an error code.
[8820544]892 *
893 */
[eadaeae8]894errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
[8820544]895{
896 // TODO: Remove entry from hash table
897 // to avoid memory leak
[a35b458]898
[eadaeae8]899 return ipc_irq_unsubscribe(ihandle);
[8820544]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 *
[cde999a]908 * @return Zero on success or an error code.
[8820544]909 *
910 */
[b7fd2a0]911errno_t async_event_subscribe(event_type_t evno,
[8820544]912 async_notification_handler_t handler, void *data)
913{
[3b1cc8d]914 notification_t *notification = notification_create(handler, data);
[8820544]915 if (!notification)
916 return ENOMEM;
[a35b458]917
[3b1cc8d]918 return ipc_event_subscribe(evno, notification->imethod);
[8820544]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 *
[cde999a]927 * @return Zero on success or an error code.
[8820544]928 *
929 */
[b7fd2a0]930errno_t async_event_task_subscribe(event_task_type_t evno,
[8820544]931 async_notification_handler_t handler, void *data)
932{
[3b1cc8d]933 notification_t *notification = notification_create(handler, data);
[8820544]934 if (!notification)
935 return ENOMEM;
[a35b458]936
[3b1cc8d]937 return ipc_event_task_subscribe(evno, notification->imethod);
[8820544]938}
939
940/** Unmask event notifications.
941 *
942 * @param evno Event type to unmask.
943 *
944 * @return Value returned by the kernel.
945 *
946 */
[b7fd2a0]947errno_t async_event_unmask(event_type_t evno)
[8820544]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 */
[b7fd2a0]959errno_t async_event_task_unmask(event_task_type_t evno)
[8820544]960{
961 return ipc_event_task_unmask(evno);
962}
963
[e70bfa5]964/** Return new incoming message for the current (fibril-local) connection.
965 *
[984a9ba]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.
[e70bfa5]972 *
973 */
[984a9ba]974bool async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
[80649a91]975{
[79ae36dd]976 assert(call);
977 assert(fibril_connection);
[a35b458]978
[7c3fb9b]979 /*
980 * Why doing this?
[79ae36dd]981 * GCC 4.1.0 coughs on fibril_connection-> dereference.
[6c46350]982 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
[c07544d3]983 * I would never expect to find so many errors in
984 * a compiler.
[6c46350]985 */
[79ae36dd]986 connection_t *conn = fibril_connection;
[a35b458]987
[95838f1]988 futex_lock(&async_futex);
[a35b458]989
[49d072e]990 if (usecs) {
[45cbcaf4]991 getuptime(&conn->wdata.to_event.expires);
[7f9d97f3]992 tv_add_diff(&conn->wdata.to_event.expires, usecs);
[c07544d3]993 } else
[f53cc81]994 conn->wdata.to_event.inlist = false;
[a35b458]995
[e70bfa5]996 /* If nothing in queue, wait until something arrives */
[6c46350]997 while (list_empty(&conn->msg_queue)) {
[01c3bb4]998 if (conn->close_chandle) {
[8c8f8d6]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
[47b7006]1004 * IPC_M_PHONE_HUNGUP until the caller notices.
[8c8f8d6]1005 */
1006 memset(call, 0, sizeof(ipc_call_t));
[228e490]1007 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
[95838f1]1008 futex_unlock(&async_futex);
[984a9ba]1009 return true;
[8c8f8d6]1010 }
[a35b458]1011
[085bd54]1012 if (usecs)
[b6ee5b1]1013 async_insert_timeout(&conn->wdata);
[a35b458]1014
[c07544d3]1015 conn->wdata.active = false;
[a35b458]1016
[c7509e5]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 */
[ab6edb6]1023 fibril_switch(FIBRIL_FROM_BLOCKED);
[a35b458]1024
[3bacee1]1025 if ((usecs) && (conn->wdata.to_event.occurred) &&
1026 (list_empty(&conn->msg_queue))) {
[e70bfa5]1027 /* If we timed out -> exit */
[95838f1]1028 futex_unlock(&async_futex);
[984a9ba]1029 return false;
[49d072e]1030 }
[450cd3a]1031 }
[a35b458]1032
[57dea62]1033 msg_t *msg = list_get_instance(list_first(&conn->msg_queue),
1034 msg_t, link);
[80649a91]1035 list_remove(&msg->link);
[a35b458]1036
[80649a91]1037 *call = msg->call;
1038 free(msg);
[a35b458]1039
[95838f1]1040 futex_unlock(&async_futex);
[984a9ba]1041 return true;
[80649a91]1042}
1043
[455f190]1044void *async_get_client_data(void)
1045{
1046 assert(fibril_connection);
1047 return fibril_connection->client->data;
1048}
1049
[e2ab36f1]1050void *async_get_client_data_by_id(task_id_t client_id)
[455f190]1051{
[e2ab36f1]1052 client_t *client = async_client_get(client_id, false);
[455f190]1053 if (!client)
1054 return NULL;
[a35b458]1055
[455f190]1056 if (!client->data) {
1057 async_client_put(client);
1058 return NULL;
1059 }
[a35b458]1060
[455f190]1061 return client->data;
1062}
1063
[e2ab36f1]1064void async_put_client_data_by_id(task_id_t client_id)
[455f190]1065{
[e2ab36f1]1066 client_t *client = async_client_get(client_id, false);
[a35b458]1067
[455f190]1068 assert(client);
1069 assert(client->data);
[a35b458]1070
[cdc8ee2d]1071 /* Drop the reference we got in async_get_client_data_by_hash(). */
1072 async_client_put(client);
[a35b458]1073
[cdc8ee2d]1074 /* Drop our own reference we got at the beginning of this function. */
[455f190]1075 async_client_put(client);
1076}
1077
[36c9234]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 *
[061274f]1083 * @param call Data of the incoming call.
[6b21292]1084 *
[36c9234]1085 */
[061274f]1086static void handle_call(ipc_call_t *call)
[80649a91]1087{
[79ae36dd]1088 assert(call);
[a35b458]1089
[d054ad3]1090 if (call->flags & IPC_CALL_ANSWERED) {
1091 /* Answer to a call made by us. */
1092 async_reply_received(call);
[e768aea]1093 return;
[d054ad3]1094 }
[e768aea]1095
[061274f]1096 if (call->cap_handle == CAP_NIL) {
[e768aea]1097 if (call->flags & IPC_CALL_NOTIF) {
1098 /* Kernel notification */
1099 queue_notification(call);
1100 }
[47b7006]1101 return;
[6b21292]1102 }
[a35b458]1103
[566992e1]1104 /* New connection */
1105 if (IPC_GET_IMETHOD(*call) == IPC_M_CONNECT_ME_TO) {
1106 iface_t iface = (iface_t) IPC_GET_ARG1(*call);
1107 sysarg_t in_phone_hash = IPC_GET_ARG5(*call);
[a35b458]1108
[49a796f1]1109 // TODO: Currently ignores all ports but the first one.
1110 void *data;
1111 async_port_handler_t handler =
1112 async_get_port_handler(iface, 0, &data);
[a35b458]1113
[061274f]1114 async_new_connection(call->in_task_id, in_phone_hash, call,
1115 handler, data);
[566992e1]1116 return;
1117 }
[a35b458]1118
[36c9234]1119 /* Try to route the call through the connection hash table */
[061274f]1120 if (route_call(call))
[47b7006]1121 return;
[a35b458]1122
[44c6d88d]1123 /* Unknown call from unknown phone - hang it up */
[061274f]1124 ipc_answer_0(call->cap_handle, EHANGUP);
[450cd3a]1125}
1126
[f2f0392]1127/** Fire all timeouts that expired. */
[e768aea]1128static suseconds_t handle_expired_timeouts(unsigned int *flags)
[c042bdd]1129{
[e768aea]1130 /* Make sure the async_futex is held. */
1131 futex_assert_is_locked(&async_futex);
1132
[c042bdd]1133 struct timeval tv;
[45cbcaf4]1134 getuptime(&tv);
[a35b458]1135
[e768aea]1136 bool fired = false;
[a35b458]1137
[b72efe8]1138 link_t *cur = list_first(&timeout_list);
1139 while (cur != NULL) {
[47b7006]1140 awaiter_t *waiter =
1141 list_get_instance(cur, awaiter_t, to_event.link);
[a35b458]1142
[e768aea]1143 if (tv_gt(&waiter->to_event.expires, &tv)) {
1144 if (fired) {
1145 *flags = SYNCH_FLAGS_NON_BLOCKING;
1146 return 0;
1147 }
1148 *flags = 0;
1149 return tv_sub_diff(&waiter->to_event.expires, &tv);
1150 }
[a35b458]1151
[f53cc81]1152 list_remove(&waiter->to_event.link);
1153 waiter->to_event.inlist = false;
1154 waiter->to_event.occurred = true;
[a35b458]1155
[36c9234]1156 /*
[c07544d3]1157 * Redundant condition?
1158 * The fibril should not be active when it gets here.
[c042bdd]1159 */
[49d072e]1160 if (!waiter->active) {
[c07544d3]1161 waiter->active = true;
[bc1f1c2]1162 fibril_add_ready(waiter->fid);
[e768aea]1163 fired = true;
[c042bdd]1164 }
[a35b458]1165
[b72efe8]1166 cur = list_first(&timeout_list);
[c042bdd]1167 }
[a35b458]1168
[e768aea]1169 if (fired) {
1170 *flags = SYNCH_FLAGS_NON_BLOCKING;
1171 return 0;
1172 }
1173
1174 return SYNCH_NO_TIMEOUT;
[c042bdd]1175}
1176
[36c9234]1177/** Endless loop dispatching incoming calls and answers.
1178 *
[c07544d3]1179 * @return Never returns.
1180 *
[36c9234]1181 */
[b7fd2a0]1182static errno_t async_manager_worker(void)
[80649a91]1183{
[c07544d3]1184 while (true) {
[95838f1]1185 futex_lock(&async_futex);
[ab6edb6]1186 fibril_switch(FIBRIL_FROM_MANAGER);
1187
1188 /*
1189 * The switch only returns when there is no non-manager fibril
1190 * it can run.
1191 */
[a35b458]1192
[1db6dfd]1193 unsigned int flags = SYNCH_FLAGS_NONE;
[e768aea]1194 suseconds_t next_timeout = handle_expired_timeouts(&flags);
1195 futex_unlock(&async_futex);
[a35b458]1196
[8619f25]1197 atomic_inc(&threads_in_ipc_wait);
[a35b458]1198
[c07544d3]1199 ipc_call_t call;
[d054ad3]1200 errno_t rc = ipc_wait(&call, next_timeout, flags);
[a35b458]1201
[8619f25]1202 atomic_dec(&threads_in_ipc_wait);
[a35b458]1203
[acf6b55]1204 if (rc == EOK)
1205 handle_call(&call);
[80649a91]1206 }
[01c3bb4]1207
[a46da63]1208 return 0;
[80649a91]1209}
1210
[36c9234]1211/** Function to start async_manager as a standalone fibril.
[c07544d3]1212 *
[36c9234]1213 * When more kernel threads are used, one async manager should exist per thread.
1214 *
[c07544d3]1215 * @param arg Unused.
1216 * @return Never returns.
[36c9234]1217 *
[a2cd194]1218 */
[b7fd2a0]1219static errno_t async_manager_fibril(void *arg)
[80649a91]1220{
[085bd54]1221 async_manager_worker();
[a46da63]1222 return 0;
[80649a91]1223}
[450cd3a]1224
[36c9234]1225/** Add one manager to manager list. */
[80649a91]1226void async_create_manager(void)
[450cd3a]1227{
[c170438]1228 fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
[86d7bfa]1229 if (fid != 0)
1230 fibril_add_manager(fid);
[80649a91]1231}
1232
1233/** Remove one manager from manager list */
1234void async_destroy_manager(void)
1235{
[bc1f1c2]1236 fibril_remove_manager();
[80649a91]1237}
1238
[36c9234]1239/** Initialize the async framework.
1240 *
1241 */
[49a796f1]1242void __async_server_init(void)
[80649a91]1243{
[062d900]1244 if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
[47b7006]1245 abort();
[a35b458]1246
[062d900]1247 if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
[47b7006]1248 abort();
[a35b458]1249
[8820544]1250 if (!hash_table_create(&notification_hash_table, 0, 0,
1251 &notification_hash_table_ops))
1252 abort();
[49a796f1]1253}
[a35b458]1254
[984a9ba]1255errno_t async_answer_0(ipc_call_t *call, errno_t retval)
[49a796f1]1256{
[984a9ba]1257 return ipc_answer_0(call->cap_handle, retval);
[450cd3a]1258}
[01ff41c]1259
[984a9ba]1260errno_t async_answer_1(ipc_call_t *call, errno_t retval, sysarg_t arg1)
[01ff41c]1261{
[984a9ba]1262 return ipc_answer_1(call->cap_handle, retval, arg1);
[49a796f1]1263}
[a35b458]1264
[984a9ba]1265errno_t async_answer_2(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1266 sysarg_t arg2)
1267{
[984a9ba]1268 return ipc_answer_2(call->cap_handle, retval, arg1, arg2);
[49a796f1]1269}
[a35b458]1270
[984a9ba]1271errno_t async_answer_3(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1272 sysarg_t arg2, sysarg_t arg3)
1273{
[984a9ba]1274 return ipc_answer_3(call->cap_handle, retval, arg1, arg2, arg3);
[49a796f1]1275}
[a35b458]1276
[984a9ba]1277errno_t async_answer_4(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1278 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1279{
[984a9ba]1280 return ipc_answer_4(call->cap_handle, retval, arg1, arg2, arg3, arg4);
[49a796f1]1281}
[a35b458]1282
[984a9ba]1283errno_t async_answer_5(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1284 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
1285{
[984a9ba]1286 return ipc_answer_5(call->cap_handle, retval, arg1, arg2, arg3, arg4,
1287 arg5);
[49a796f1]1288}
[a35b458]1289
[984a9ba]1290errno_t async_forward_fast(ipc_call_t *call, async_exch_t *exch,
[49a796f1]1291 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
1292{
[984a9ba]1293 assert(call);
1294
[49a796f1]1295 if (exch == NULL)
1296 return ENOENT;
[a35b458]1297
[984a9ba]1298 return ipc_forward_fast(call->cap_handle, exch->phone, imethod, arg1,
1299 arg2, mode);
[49a796f1]1300}
[a35b458]1301
[984a9ba]1302errno_t async_forward_slow(ipc_call_t *call, async_exch_t *exch,
[49a796f1]1303 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
1304 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
1305{
[984a9ba]1306 assert(call);
1307
[49a796f1]1308 if (exch == NULL)
1309 return ENOENT;
[a35b458]1310
[984a9ba]1311 return ipc_forward_slow(call->cap_handle, exch->phone, imethod, arg1,
1312 arg2, arg3, arg4, arg5, mode);
[01ff41c]1313}
1314
[49a796f1]1315/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
[36c9234]1316 *
[49a796f1]1317 * Ask through phone for a new connection to some service.
[01ff41c]1318 *
[984a9ba]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.
[c07544d3]1323 *
[49a796f1]1324 * @return Zero on success or an error code.
[36c9234]1325 *
[01ff41c]1326 */
[49a796f1]1327errno_t async_connect_to_me(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
1328 sysarg_t arg3)
[01ff41c]1329{
[79ae36dd]1330 if (exch == NULL)
[49a796f1]1331 return ENOENT;
[a35b458]1332
[49a796f1]1333 ipc_call_t answer;
1334 aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
1335 &answer);
[a35b458]1336
[49a796f1]1337 errno_t rc;
1338 async_wait_for(req, &rc);
1339 if (rc != EOK)
1340 return (errno_t) rc;
[a35b458]1341
[49a796f1]1342 return EOK;
1343}
[a35b458]1344
[49a796f1]1345/** Interrupt one thread of this task from waiting for IPC. */
1346void async_poke(void)
1347{
[c8afd5a]1348 if (atomic_get(&threads_in_ipc_wait) > 0)
1349 ipc_poke();
[01ff41c]1350}
1351
[49a796f1]1352/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
[0da4e41]1353 *
[47b7006]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.
[0da4e41]1357 *
1358 * So far, this wrapper is to be used from within a connection fibril.
1359 *
[984a9ba]1360 * @param call Storage for the data of the IPC_M_SHARE_IN call.
1361 * @param size Destination address space area size.
[47b7006]1362 *
1363 * @return True on success, false on failure.
[0da4e41]1364 *
1365 */
[984a9ba]1366bool async_share_in_receive(ipc_call_t *call, size_t *size)
[0da4e41]1367{
[984a9ba]1368 assert(call);
[0da4e41]1369 assert(size);
[a35b458]1370
[984a9ba]1371 async_get_call(call);
[a35b458]1372
[984a9ba]1373 if (IPC_GET_IMETHOD(*call) != IPC_M_SHARE_IN)
[47b7006]1374 return false;
[a35b458]1375
[984a9ba]1376 *size = (size_t) IPC_GET_ARG1(*call);
[47b7006]1377 return true;
[0da4e41]1378}
1379
1380/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
1381 *
[fbcdeb8]1382 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
[47b7006]1383 * calls so that the user doesn't have to remember the meaning of each IPC
1384 * argument.
[0da4e41]1385 *
[984a9ba]1386 * @param call IPC_M_DATA_READ call to answer.
1387 * @param src Source address space base.
1388 * @param flags Flags to be used for sharing. Bits can be only cleared.
[47b7006]1389 *
1390 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1391 *
1392 */
[984a9ba]1393errno_t async_share_in_finalize(ipc_call_t *call, void *src, unsigned int flags)
[0da4e41]1394{
[984a9ba]1395 assert(call);
1396
[a69d42e]1397 // FIXME: The source has no business deciding destination address.
[984a9ba]1398 return ipc_answer_3(call->cap_handle, EOK, (sysarg_t) src, (sysarg_t) flags,
[2eadda9]1399 (sysarg_t) __progsymbols.end);
[0da4e41]1400}
1401
1402/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
1403 *
[47b7006]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.
[0da4e41]1407 *
1408 * So far, this wrapper is to be used from within a connection fibril.
1409 *
[984a9ba]1410 * @param call Storage for the data 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.
[47b7006]1413 *
1414 * @return True on success, false on failure.
[0da4e41]1415 *
1416 */
[984a9ba]1417bool async_share_out_receive(ipc_call_t *call, size_t *size,
[01c3bb4]1418 unsigned int *flags)
[0da4e41]1419{
[984a9ba]1420 assert(call);
[0da4e41]1421 assert(size);
1422 assert(flags);
[a35b458]1423
[984a9ba]1424 async_get_call(call);
[a35b458]1425
[984a9ba]1426 if (IPC_GET_IMETHOD(*call) != IPC_M_SHARE_OUT)
[47b7006]1427 return false;
[a35b458]1428
[984a9ba]1429 *size = (size_t) IPC_GET_ARG2(*call);
1430 *flags = (unsigned int) IPC_GET_ARG3(*call);
[47b7006]1431 return true;
[0da4e41]1432}
1433
1434/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
1435 *
[47b7006]1436 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
1437 * calls so that the user doesn't have to remember the meaning of each IPC
1438 * argument.
[0da4e41]1439 *
[984a9ba]1440 * @param call IPC_M_DATA_WRITE call to answer.
1441 * @param dst Address of the storage for the destination address space area
1442 * base address.
[47b7006]1443 *
[01c3bb4]1444 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1445 *
1446 */
[984a9ba]1447errno_t async_share_out_finalize(ipc_call_t *call, void **dst)
[0da4e41]1448{
[984a9ba]1449 assert(call);
[0da4e41]1450
[2eadda9]1451 return ipc_answer_2(call->cap_handle, EOK, (sysarg_t) __progsymbols.end,
[984a9ba]1452 (sysarg_t) dst);
[d768d4c8]1453}
1454
1455/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1456 *
1457 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
1458 * calls so that the user doesn't have to remember the meaning of each IPC
1459 * argument.
1460 *
1461 * So far, this wrapper is to be used from within a connection fibril.
1462 *
[984a9ba]1463 * @param call Storage for the data of the IPC_M_DATA_READ.
1464 * @param size Storage for the maximum size. Can be NULL.
[d768d4c8]1465 *
1466 * @return True on success, false on failure.
1467 *
1468 */
[984a9ba]1469bool async_data_read_receive(ipc_call_t *call, size_t *size)
[0da4e41]1470{
[984a9ba]1471 assert(call);
[a35b458]1472
[984a9ba]1473 async_get_call(call);
[a35b458]1474
[984a9ba]1475 if (IPC_GET_IMETHOD(*call) != IPC_M_DATA_READ)
[47b7006]1476 return false;
[a35b458]1477
[0da4e41]1478 if (size)
[984a9ba]1479 *size = (size_t) IPC_GET_ARG2(*call);
[a35b458]1480
[47b7006]1481 return true;
[0da4e41]1482}
1483
1484/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
1485 *
[47b7006]1486 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
1487 * calls so that the user doesn't have to remember the meaning of each IPC
1488 * argument.
[0da4e41]1489 *
[984a9ba]1490 * @param call IPC_M_DATA_READ call to answer.
1491 * @param src Source address for the IPC_M_DATA_READ call.
1492 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
1493 * the maximum size announced by the sender.
[47b7006]1494 *
[01c3bb4]1495 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1496 *
1497 */
[984a9ba]1498errno_t async_data_read_finalize(ipc_call_t *call, const void *src, size_t size)
[0da4e41]1499{
[984a9ba]1500 assert(call);
1501
1502 return ipc_answer_2(call->cap_handle, EOK, (sysarg_t) src,
1503 (sysarg_t) size);
[0da4e41]1504}
1505
[b4cbef1]1506/** Wrapper for forwarding any read request
1507 *
1508 */
[b7fd2a0]1509errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
[79ae36dd]1510 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1511 ipc_call_t *dataptr)
[b4cbef1]1512{
[79ae36dd]1513 if (exch == NULL)
1514 return ENOENT;
[a35b458]1515
[984a9ba]1516 ipc_call_t call;
1517 if (!async_data_read_receive(&call, NULL)) {
1518 async_answer_0(&call, EINVAL);
[b4cbef1]1519 return EINVAL;
1520 }
[a35b458]1521
[79ae36dd]1522 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]1523 dataptr);
1524 if (msg == 0) {
[984a9ba]1525 async_answer_0(&call, EINVAL);
[b4cbef1]1526 return EINVAL;
1527 }
[a35b458]1528
[984a9ba]1529 errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
[b4cbef1]1530 IPC_FF_ROUTE_FROM_ME);
1531 if (retval != EOK) {
[ab9f443]1532 async_forget(msg);
[984a9ba]1533 async_answer_0(&call, retval);
[b4cbef1]1534 return retval;
1535 }
[a35b458]1536
[b7fd2a0]1537 errno_t rc;
[b4cbef1]1538 async_wait_for(msg, &rc);
[a35b458]1539
[b7fd2a0]1540 return (errno_t) rc;
[b4cbef1]1541}
1542
[0da4e41]1543/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1544 *
[47b7006]1545 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
1546 * calls so that the user doesn't have to remember the meaning of each IPC
1547 * argument.
[0da4e41]1548 *
1549 * So far, this wrapper is to be used from within a connection fibril.
1550 *
[984a9ba]1551 * @param call Storage for the data of the IPC_M_DATA_WRITE.
1552 * @param size Storage for the suggested size. May be NULL.
[5ae1c51]1553 *
1554 * @return True on success, false on failure.
1555 *
1556 */
[984a9ba]1557bool async_data_write_receive(ipc_call_t *call, size_t *size)
[0da4e41]1558{
[984a9ba]1559 assert(call);
[a35b458]1560
[984a9ba]1561 async_get_call(call);
[a35b458]1562
[984a9ba]1563 if (IPC_GET_IMETHOD(*call) != IPC_M_DATA_WRITE)
[47b7006]1564 return false;
[a35b458]1565
[0da4e41]1566 if (size)
[984a9ba]1567 *size = (size_t) IPC_GET_ARG2(*call);
[a35b458]1568
[47b7006]1569 return true;
[0da4e41]1570}
1571
1572/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
1573 *
[47b7006]1574 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
1575 * calls so that the user doesn't have to remember the meaning of each IPC
1576 * argument.
[0da4e41]1577 *
[984a9ba]1578 * @param call IPC_M_DATA_WRITE call to answer.
1579 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
1580 * @param size Final size for the IPC_M_DATA_WRITE call.
[b4cbef1]1581 *
[01c3bb4]1582 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1583 *
1584 */
[984a9ba]1585errno_t async_data_write_finalize(ipc_call_t *call, void *dst, size_t size)
[0da4e41]1586{
[984a9ba]1587 assert(call);
1588
1589 return async_answer_2(call, EOK, (sysarg_t) dst, (sysarg_t) size);
[0da4e41]1590}
1591
[eda925a]1592/** Wrapper for receiving binary data or strings
[8aa42e3]1593 *
1594 * This wrapper only makes it more comfortable to use async_data_write_*
[eda925a]1595 * functions to receive binary data or strings.
[8aa42e3]1596 *
[472c09d]1597 * @param data Pointer to data pointer (which should be later disposed
1598 * by free()). If the operation fails, the pointer is not
1599 * touched.
[eda925a]1600 * @param nullterm If true then the received data is always zero terminated.
1601 * This also causes to allocate one extra byte beyond the
1602 * raw transmitted data.
[b4cbef1]1603 * @param min_size Minimum size (in bytes) of the data to receive.
[472c09d]1604 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
1605 * no limit.
[eda925a]1606 * @param granulariy If non-zero then the size of the received data has to
[472c09d]1607 * be divisible by this value.
1608 * @param received If not NULL, the size of the received data is stored here.
[8aa42e3]1609 *
1610 * @return Zero on success or a value from @ref errno.h on failure.
1611 *
1612 */
[b7fd2a0]1613errno_t async_data_write_accept(void **data, const bool nullterm,
[eda925a]1614 const size_t min_size, const size_t max_size, const size_t granularity,
1615 size_t *received)
[8aa42e3]1616{
[79ae36dd]1617 assert(data);
[a35b458]1618
[984a9ba]1619 ipc_call_t call;
[8aa42e3]1620 size_t size;
[984a9ba]1621 if (!async_data_write_receive(&call, &size)) {
1622 async_answer_0(&call, EINVAL);
[8aa42e3]1623 return EINVAL;
1624 }
[a35b458]1625
[b4cbef1]1626 if (size < min_size) {
[984a9ba]1627 async_answer_0(&call, EINVAL);
[b4cbef1]1628 return EINVAL;
1629 }
[a35b458]1630
[8aa42e3]1631 if ((max_size > 0) && (size > max_size)) {
[984a9ba]1632 async_answer_0(&call, EINVAL);
[8aa42e3]1633 return EINVAL;
1634 }
[a35b458]1635
[472c09d]1636 if ((granularity > 0) && ((size % granularity) != 0)) {
[984a9ba]1637 async_answer_0(&call, EINVAL);
[472c09d]1638 return EINVAL;
1639 }
[a35b458]1640
[57dea62]1641 void *arg_data;
[a35b458]1642
[eda925a]1643 if (nullterm)
[57dea62]1644 arg_data = malloc(size + 1);
[eda925a]1645 else
[57dea62]1646 arg_data = malloc(size);
[a35b458]1647
[57dea62]1648 if (arg_data == NULL) {
[984a9ba]1649 async_answer_0(&call, ENOMEM);
[8aa42e3]1650 return ENOMEM;
1651 }
[a35b458]1652
[984a9ba]1653 errno_t rc = async_data_write_finalize(&call, arg_data, size);
[8aa42e3]1654 if (rc != EOK) {
[57dea62]1655 free(arg_data);
[8aa42e3]1656 return rc;
1657 }
[a35b458]1658
[eda925a]1659 if (nullterm)
[57dea62]1660 ((char *) arg_data)[size] = 0;
[a35b458]1661
[57dea62]1662 *data = arg_data;
[472c09d]1663 if (received != NULL)
1664 *received = size;
[a35b458]1665
[8aa42e3]1666 return EOK;
1667}
1668
[b4cbef1]1669/** Wrapper for voiding any data that is about to be received
1670 *
1671 * This wrapper can be used to void any pending data
1672 *
1673 * @param retval Error value from @ref errno.h to be returned to the caller.
1674 *
1675 */
[b7fd2a0]1676void async_data_write_void(errno_t retval)
[b4cbef1]1677{
[984a9ba]1678 ipc_call_t call;
1679 async_data_write_receive(&call, NULL);
1680 async_answer_0(&call, retval);
[b4cbef1]1681}
1682
1683/** Wrapper for forwarding any data that is about to be received
1684 *
1685 */
[b7fd2a0]1686errno_t async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
[79ae36dd]1687 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1688 ipc_call_t *dataptr)
[b4cbef1]1689{
[79ae36dd]1690 if (exch == NULL)
1691 return ENOENT;
[a35b458]1692
[984a9ba]1693 ipc_call_t call;
1694 if (!async_data_write_receive(&call, NULL)) {
1695 async_answer_0(&call, EINVAL);
[b4cbef1]1696 return EINVAL;
1697 }
[a35b458]1698
[79ae36dd]1699 aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]1700 dataptr);
1701 if (msg == 0) {
[984a9ba]1702 async_answer_0(&call, EINVAL);
[b4cbef1]1703 return EINVAL;
1704 }
[a35b458]1705
[984a9ba]1706 errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
[b4cbef1]1707 IPC_FF_ROUTE_FROM_ME);
1708 if (retval != EOK) {
[ab9f443]1709 async_forget(msg);
[984a9ba]1710 async_answer_0(&call, retval);
[b4cbef1]1711 return retval;
1712 }
[a35b458]1713
[b7fd2a0]1714 errno_t rc;
[b4cbef1]1715 async_wait_for(msg, &rc);
[a35b458]1716
[b7fd2a0]1717 return (errno_t) rc;
[b4cbef1]1718}
1719
[79ae36dd]1720/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1721 *
1722 * If the current call is IPC_M_CONNECT_TO_ME then a new
1723 * async session is created for the accepted phone.
1724 *
1725 * @param mgmt Exchange management style.
1726 *
[8869f7b]1727 * @return New async session.
1728 * @return NULL on failure.
[79ae36dd]1729 *
1730 */
1731async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
1732{
1733 /* Accept the phone */
1734 ipc_call_t call;
[984a9ba]1735 async_get_call(&call);
1736
[eadaeae8]1737 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(call);
[a35b458]1738
[eadaeae8]1739 if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) ||
1740 !CAP_HANDLE_VALID((phandle))) {
[984a9ba]1741 async_answer_0(&call, EINVAL);
[79ae36dd]1742 return NULL;
1743 }
[a35b458]1744
[79ae36dd]1745 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1746 if (sess == NULL) {
[984a9ba]1747 async_answer_0(&call, ENOMEM);
[79ae36dd]1748 return NULL;
1749 }
[a35b458]1750
[566992e1]1751 sess->iface = 0;
[79ae36dd]1752 sess->mgmt = mgmt;
[01c3bb4]1753 sess->phone = phandle;
[79ae36dd]1754 sess->arg1 = 0;
1755 sess->arg2 = 0;
1756 sess->arg3 = 0;
[a35b458]1757
[58cbf8d5]1758 fibril_mutex_initialize(&sess->remote_state_mtx);
1759 sess->remote_state_data = NULL;
[a35b458]1760
[79ae36dd]1761 list_initialize(&sess->exch_list);
1762 fibril_mutex_initialize(&sess->mutex);
1763 atomic_set(&sess->refcnt, 0);
[a35b458]1764
[79ae36dd]1765 /* Acknowledge the connected phone */
[984a9ba]1766 async_answer_0(&call, EOK);
[a35b458]1767
[79ae36dd]1768 return sess;
1769}
1770
[8869f7b]1771/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1772 *
1773 * If the call is IPC_M_CONNECT_TO_ME then a new
1774 * async session is created. However, the phone is
1775 * not accepted automatically.
1776 *
1777 * @param mgmt Exchange management style.
1778 * @param call Call data.
1779 *
1780 * @return New async session.
1781 * @return NULL on failure.
1782 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
1783 *
1784 */
1785async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
1786{
[eadaeae8]1787 cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(*call);
[a35b458]1788
[eadaeae8]1789 if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) ||
1790 !CAP_HANDLE_VALID((phandle)))
[8869f7b]1791 return NULL;
[a35b458]1792
[8869f7b]1793 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
1794 if (sess == NULL)
1795 return NULL;
[a35b458]1796
[566992e1]1797 sess->iface = 0;
[8869f7b]1798 sess->mgmt = mgmt;
[01c3bb4]1799 sess->phone = phandle;
[8869f7b]1800 sess->arg1 = 0;
1801 sess->arg2 = 0;
1802 sess->arg3 = 0;
[a35b458]1803
[58cbf8d5]1804 fibril_mutex_initialize(&sess->remote_state_mtx);
1805 sess->remote_state_data = NULL;
[a35b458]1806
[8869f7b]1807 list_initialize(&sess->exch_list);
1808 fibril_mutex_initialize(&sess->mutex);
1809 atomic_set(&sess->refcnt, 0);
[a35b458]1810
[8869f7b]1811 return sess;
1812}
1813
[984a9ba]1814bool async_state_change_receive(ipc_call_t *call)
[2c4aa39]1815{
[984a9ba]1816 assert(call);
[a35b458]1817
[984a9ba]1818 async_get_call(call);
[a35b458]1819
[984a9ba]1820 if (IPC_GET_IMETHOD(*call) != IPC_M_STATE_CHANGE_AUTHORIZE)
[2c4aa39]1821 return false;
[a35b458]1822
[2c4aa39]1823 return true;
1824}
1825
[984a9ba]1826errno_t async_state_change_finalize(ipc_call_t *call, async_exch_t *other_exch)
[2c4aa39]1827{
[984a9ba]1828 assert(call);
1829
1830 return async_answer_1(call, EOK, CAP_HANDLE_RAW(other_exch->phone));
[2c4aa39]1831}
1832
[6b96dc06]1833__noreturn void async_manager(void)
[d73d992]1834{
[95838f1]1835 futex_lock(&async_futex);
[d73d992]1836 fibril_switch(FIBRIL_FROM_DEAD);
1837 __builtin_unreachable();
1838}
1839
[a46da63]1840/** @}
[b2951e2]1841 */
Note: See TracBrowser for help on using the repository browser.