source: mainline/uspace/lib/c/generic/async.c@ 3c22f70

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3c22f70 was 3c22f70, checked in by Jakub Jermar <jakub@…>, 14 years ago

Pass client task hash into async_new_connection().

  • Property mode set to 100644
File size: 41.1 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 *
[9591265]42 * You should be able to write very simple multithreaded programs, the async
43 * framework will automatically take care of most synchronization problems.
[80649a91]44 *
45 * Default semantics:
[c07544d3]46 * - async_send_*(): Send asynchronously. If the kernel refuses to send
47 * more messages, [ try to get responses from kernel, if
48 * nothing found, might try synchronous ]
[80649a91]49 *
[9591265]50 * Example of use (pseudo C):
[c07544d3]51 *
[80649a91]52 * 1) Multithreaded client application
[9591265]53 *
[c07544d3]54 * fibril_create(fibril1, ...);
55 * fibril_create(fibril2, ...);
56 * ...
57 *
58 * int fibril1(void *arg)
59 * {
60 * conn = ipc_connect_me_to();
61 * c1 = async_send(conn);
62 * c2 = async_send(conn);
63 * async_wait_for(c1);
64 * async_wait_for(c2);
65 * ...
66 * }
[80649a91]67 *
68 *
69 * 2) Multithreaded server application
70 *
[c07544d3]71 * main()
72 * {
73 * async_manager();
74 * }
75 *
76 * my_client_connection(icallid, *icall)
77 * {
78 * if (want_refuse) {
79 * ipc_answer_0(icallid, ELIMIT);
80 * return;
81 * }
82 * ipc_answer_0(icallid, EOK);
[80649a91]83 *
[c07544d3]84 * callid = async_get_call(&call);
[0772aff]85 * somehow_handle_the_call(callid, call);
[c07544d3]86 * ipc_answer_2(callid, 1, 2, 3);
[53ca318]87 *
[c07544d3]88 * callid = async_get_call(&call);
89 * ...
90 * }
[a2cd194]91 *
[80649a91]92 */
[9591265]93
[80649a91]94#include <futex.h>
95#include <async.h>
[4f5edcf6]96#include <async_priv.h>
[bc1f1c2]97#include <fibril.h>
[80649a91]98#include <stdio.h>
[d9c8c81]99#include <adt/hash_table.h>
100#include <adt/list.h>
[80649a91]101#include <ipc/ipc.h>
102#include <assert.h>
103#include <errno.h>
[daa90e8]104#include <sys/time.h>
[c042bdd]105#include <arch/barrier.h>
[0cc4313]106#include <bool.h>
[80649a91]107
[fc42b28]108atomic_t async_futex = FUTEX_INITIALIZER;
[80649a91]109
[8619f25]110/** Number of threads waiting for IPC in the kernel. */
111atomic_t threads_in_ipc_wait = { 0 };
112
[49d072e]113typedef struct {
114 awaiter_t wdata;
[e70bfa5]115
116 /** If reply was received. */
[c07544d3]117 bool done;
118
[e70bfa5]119 /** Pointer to where the answer data is stored. */
[c07544d3]120 ipc_call_t *dataptr;
121
[96b02eb9]122 sysarg_t retval;
[01ff41c]123} amsg_t;
124
[36c9234]125/**
126 * Structures of this type are used to group information about a call and a
127 * message queue link.
128 */
[80649a91]129typedef struct {
130 link_t link;
131 ipc_callid_t callid;
132 ipc_call_t call;
133} msg_t;
134
135typedef struct {
[49d072e]136 awaiter_t wdata;
[c07544d3]137
[e70bfa5]138 /** Hash table link. */
139 link_t link;
[c07544d3]140
[3c22f70]141 /** Incoming client task hash. */
142 sysarg_t in_task_hash;
[e70bfa5]143 /** Incoming phone hash. */
[96b02eb9]144 sysarg_t in_phone_hash;
[c07544d3]145
[e70bfa5]146 /** Messages that should be delivered to this fibril. */
[c07544d3]147 link_t msg_queue;
148
[e70bfa5]149 /** Identification of the opening call. */
[80649a91]150 ipc_callid_t callid;
[e70bfa5]151 /** Call data of the opening call. */
[80649a91]152 ipc_call_t call;
[c07544d3]153
[e70bfa5]154 /** Identification of the closing call. */
155 ipc_callid_t close_callid;
[c07544d3]156
[e70bfa5]157 /** Fibril function that will be used to handle the connection. */
[bc1f1c2]158 void (*cfibril)(ipc_callid_t, ipc_call_t *);
[80649a91]159} connection_t;
160
[bc1f1c2]161/** Identifier of the incoming connection handled by the current fibril. */
[26360f7]162fibril_local connection_t *FIBRIL_connection;
[e70bfa5]163
[da0c91e7]164static void default_client_connection(ipc_callid_t callid, ipc_call_t *call);
[51dbadf3]165static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call);
[36c9234]166
167/**
168 * Pointer to a fibril function that will be used to handle connections.
169 */
[da0c91e7]170static async_client_conn_t client_connection = default_client_connection;
[c07544d3]171
[36c9234]172/**
173 * Pointer to a fibril function that will be used to handle interrupt
174 * notifications.
175 */
[51dbadf3]176static async_client_conn_t interrupt_received = default_interrupt_received;
[da0c91e7]177
[c07544d3]178static hash_table_t conn_hash_table;
179static LIST_INITIALIZE(timeout_list);
180
181#define CONN_HASH_TABLE_CHAINS 32
[80649a91]182
[e70bfa5]183/** Compute hash into the connection hash table based on the source phone hash.
184 *
[c07544d3]185 * @param key Pointer to source phone hash.
186 *
187 * @return Index into the connection hash table.
[e70bfa5]188 *
189 */
[80649a91]190static hash_index_t conn_hash(unsigned long *key)
[450cd3a]191{
[80649a91]192 assert(key);
[c07544d3]193 return (((*key) >> 4) % CONN_HASH_TABLE_CHAINS);
[450cd3a]194}
[06502f7d]195
[e70bfa5]196/** Compare hash table item with a key.
197 *
[c07544d3]198 * @param key Array containing the source phone hash as the only item.
199 * @param keys Expected 1 but ignored.
200 * @param item Connection hash table item.
201 *
202 * @return True on match, false otherwise.
[e70bfa5]203 *
204 */
[80649a91]205static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
[450cd3a]206{
[c07544d3]207 connection_t *hs = hash_table_get_instance(item, connection_t, link);
208 return (key[0] == hs->in_phone_hash);
[450cd3a]209}
[06502f7d]210
[e70bfa5]211/** Connection hash table removal callback function.
212 *
213 * This function is called whenever a connection is removed from the connection
214 * hash table.
215 *
[c07544d3]216 * @param item Connection hash table item being removed.
217 *
[e70bfa5]218 */
[80649a91]219static void conn_remove(link_t *item)
[450cd3a]220{
[80649a91]221 free(hash_table_get_instance(item, connection_t, link));
[450cd3a]222}
223
[80649a91]224
[e70bfa5]225/** Operations for the connection hash table. */
[80649a91]226static hash_table_operations_t conn_hash_table_ops = {
227 .hash = conn_hash,
228 .compare = conn_compare,
229 .remove_callback = conn_remove
230};
231
[e70bfa5]232/** Sort in current fibril's timeout request.
[49d072e]233 *
[c07544d3]234 * @param wd Wait data of the current fibril.
235 *
[49d072e]236 */
[b6ee5b1]237void async_insert_timeout(awaiter_t *wd)
[49d072e]238{
[f53cc81]239 wd->to_event.occurred = false;
240 wd->to_event.inlist = true;
[c07544d3]241
242 link_t *tmp = timeout_list.next;
[49d072e]243 while (tmp != &timeout_list) {
[f53cc81]244 awaiter_t *cur;
[c07544d3]245
[f53cc81]246 cur = list_get_instance(tmp, awaiter_t, to_event.link);
247 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
[49d072e]248 break;
249 tmp = tmp->next;
250 }
[c07544d3]251
[f53cc81]252 list_append(&wd->to_event.link, tmp);
[49d072e]253}
254
[e70bfa5]255/** Try to route a call to an appropriate connection fibril.
[80649a91]256 *
[36c9234]257 * If the proper connection fibril is found, a message with the call is added to
258 * its message queue. If the fibril was not active, it is activated and all
259 * timeouts are unregistered.
260 *
[c07544d3]261 * @param callid Hash of the incoming call.
262 * @param call Data of the incoming call.
263 *
264 * @return False if the call doesn't match any connection.
265 * True if the call was passed to the respective connection fibril.
[36c9234]266 *
[80649a91]267 */
[c07544d3]268static bool route_call(ipc_callid_t callid, ipc_call_t *call)
[450cd3a]269{
[01ff41c]270 futex_down(&async_futex);
[c07544d3]271
272 unsigned long key = call->in_phone_hash;
273 link_t *hlp = hash_table_find(&conn_hash_table, &key);
274
[80649a91]275 if (!hlp) {
[01ff41c]276 futex_up(&async_futex);
[c07544d3]277 return false;
[450cd3a]278 }
[c07544d3]279
280 connection_t *conn = hash_table_get_instance(hlp, connection_t, link);
281
282 msg_t *msg = malloc(sizeof(*msg));
283 if (!msg) {
284 futex_up(&async_futex);
285 return false;
286 }
287
[80649a91]288 msg->callid = callid;
289 msg->call = *call;
290 list_append(&msg->link, &conn->msg_queue);
[c07544d3]291
[228e490]292 if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
[41269bd]293 conn->close_callid = callid;
[80649a91]294
[36c9234]295 /* If the connection fibril is waiting for an event, activate it */
[49d072e]296 if (!conn->wdata.active) {
[c07544d3]297
[49d072e]298 /* If in timeout list, remove it */
[f53cc81]299 if (conn->wdata.to_event.inlist) {
300 conn->wdata.to_event.inlist = false;
301 list_remove(&conn->wdata.to_event.link);
[49d072e]302 }
[c07544d3]303
304 conn->wdata.active = true;
[bc1f1c2]305 fibril_add_ready(conn->wdata.fid);
[80649a91]306 }
[c07544d3]307
[01ff41c]308 futex_up(&async_futex);
[c07544d3]309 return true;
310}
[80649a91]311
[c07544d3]312/** Notification fibril.
313 *
314 * When a notification arrives, a fibril with this implementing function is
315 * created. It calls interrupt_received() and does the final cleanup.
316 *
317 * @param arg Message structure pointer.
318 *
319 * @return Always zero.
320 *
321 */
322static int notification_fibril(void *arg)
323{
324 msg_t *msg = (msg_t *) arg;
325 interrupt_received(msg->callid, &msg->call);
326
327 free(msg);
328 return 0;
329}
330
331/** Process interrupt notification.
332 *
333 * A new fibril is created which would process the notification.
334 *
335 * @param callid Hash of the incoming call.
336 * @param call Data of the incoming call.
337 *
338 * @return False if an error occured.
339 * True if the call was passed to the notification fibril.
340 *
341 */
342static bool process_notification(ipc_callid_t callid, ipc_call_t *call)
343{
344 futex_down(&async_futex);
345
346 msg_t *msg = malloc(sizeof(*msg));
347 if (!msg) {
348 futex_up(&async_futex);
349 return false;
350 }
351
352 msg->callid = callid;
353 msg->call = *call;
354
355 fid_t fid = fibril_create(notification_fibril, msg);
356 fibril_add_ready(fid);
357
358 futex_up(&async_futex);
359 return true;
[80649a91]360}
361
[e70bfa5]362/** Return new incoming message for the current (fibril-local) connection.
363 *
[c07544d3]364 * @param call Storage where the incoming call data will be stored.
365 * @param usecs Timeout in microseconds. Zero denotes no timeout.
366 *
367 * @return If no timeout was specified, then a hash of the
368 * incoming call is returned. If a timeout is specified,
369 * then a hash of the incoming call is returned unless
370 * the timeout expires prior to receiving a message. In
371 * that case zero is returned.
[e70bfa5]372 *
373 */
[49d072e]374ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
[80649a91]375{
[bc1f1c2]376 assert(FIBRIL_connection);
[c07544d3]377
378 /* Why doing this?
379 * GCC 4.1.0 coughs on FIBRIL_connection-> dereference.
[6c46350]380 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
[c07544d3]381 * I would never expect to find so many errors in
382 * a compiler.
[6c46350]383 */
[c07544d3]384 connection_t *conn = FIBRIL_connection;
385
[01ff41c]386 futex_down(&async_futex);
[c07544d3]387
[49d072e]388 if (usecs) {
[f53cc81]389 gettimeofday(&conn->wdata.to_event.expires, NULL);
390 tv_add(&conn->wdata.to_event.expires, usecs);
[c07544d3]391 } else
[f53cc81]392 conn->wdata.to_event.inlist = false;
[c07544d3]393
[e70bfa5]394 /* If nothing in queue, wait until something arrives */
[6c46350]395 while (list_empty(&conn->msg_queue)) {
[8c8f8d6]396 if (conn->close_callid) {
397 /*
398 * Handle the case when the connection was already
399 * closed by the client but the server did not notice
400 * the first IPC_M_PHONE_HUNGUP call and continues to
401 * call async_get_call_timeout(). Repeat
402 * IPC_M_PHONE_HUNGUP until the caller notices.
403 */
404 memset(call, 0, sizeof(ipc_call_t));
[228e490]405 IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
[8c8f8d6]406 futex_up(&async_futex);
407 return conn->close_callid;
408 }
409
[085bd54]410 if (usecs)
[b6ee5b1]411 async_insert_timeout(&conn->wdata);
[c07544d3]412
413 conn->wdata.active = false;
414
[c7509e5]415 /*
416 * Note: the current fibril will be rescheduled either due to a
417 * timeout or due to an arriving message destined to it. In the
418 * former case, handle_expired_timeouts() and, in the latter
419 * case, route_call() will perform the wakeup.
420 */
[116d3f6f]421 fibril_switch(FIBRIL_TO_MANAGER);
[c07544d3]422
[e70bfa5]423 /*
[c07544d3]424 * Futex is up after getting back from async_manager.
425 * Get it again.
[c7509e5]426 */
[49d072e]427 futex_down(&async_futex);
[f53cc81]428 if ((usecs) && (conn->wdata.to_event.occurred)
[c07544d3]429 && (list_empty(&conn->msg_queue))) {
[e70bfa5]430 /* If we timed out -> exit */
[49d072e]431 futex_up(&async_futex);
432 return 0;
433 }
[450cd3a]434 }
435
[c07544d3]436 msg_t *msg = list_get_instance(conn->msg_queue.next, msg_t, link);
[80649a91]437 list_remove(&msg->link);
[c07544d3]438
439 ipc_callid_t callid = msg->callid;
[80649a91]440 *call = msg->call;
441 free(msg);
442
[01ff41c]443 futex_up(&async_futex);
[80649a91]444 return callid;
445}
446
[36c9234]447/** Default fibril function that gets called to handle new connection.
[a2cd194]448 *
[e70bfa5]449 * This function is defined as a weak symbol - to be redefined in user code.
[36c9234]450 *
[c07544d3]451 * @param callid Hash of the incoming call.
452 * @param call Data of the incoming call.
453 *
[a2cd194]454 */
[da0c91e7]455static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)
[80649a91]456{
[b74959bd]457 ipc_answer_0(callid, ENOENT);
[80649a91]458}
[36c9234]459
460/** Default fibril function that gets called to handle interrupt notifications.
461 *
[c07544d3]462 * This function is defined as a weak symbol - to be redefined in user code.
463 *
464 * @param callid Hash of the incoming call.
465 * @param call Data of the incoming call.
466 *
[36c9234]467 */
[51dbadf3]468static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
[44c6d88d]469{
470}
471
[f2f0392]472/** Wrapper for client connection fibril.
473 *
[36c9234]474 * When a new connection arrives, a fibril with this implementing function is
[f2f0392]475 * created. It calls client_connection() and does the final cleanup.
[a2cd194]476 *
[c07544d3]477 * @param arg Connection structure pointer.
478 *
479 * @return Always zero.
[a2cd194]480 *
481 */
[c07544d3]482static int connection_fibril(void *arg)
[80649a91]483{
[c07544d3]484 /*
485 * Setup fibril-local connection pointer and call client_connection().
486 *
487 */
[bc1f1c2]488 FIBRIL_connection = (connection_t *) arg;
489 FIBRIL_connection->cfibril(FIBRIL_connection->callid,
490 &FIBRIL_connection->call);
[a46da63]491
[36c9234]492 /* Remove myself from the connection hash table */
[01ff41c]493 futex_down(&async_futex);
[c07544d3]494 unsigned long key = FIBRIL_connection->in_phone_hash;
[a2cd194]495 hash_table_remove(&conn_hash_table, &key, 1);
[01ff41c]496 futex_up(&async_futex);
[a46da63]497
[36c9234]498 /* Answer all remaining messages with EHANGUP */
[bc1f1c2]499 while (!list_empty(&FIBRIL_connection->msg_queue)) {
[cc27c8c5]500 msg_t *msg;
[c07544d3]501
[cc27c8c5]502 msg = list_get_instance(FIBRIL_connection->msg_queue.next,
503 msg_t, link);
[a2cd194]504 list_remove(&msg->link);
[b74959bd]505 ipc_answer_0(msg->callid, EHANGUP);
[a2cd194]506 free(msg);
507 }
[c07544d3]508
[bc1f1c2]509 if (FIBRIL_connection->close_callid)
[b74959bd]510 ipc_answer_0(FIBRIL_connection->close_callid, EOK);
[a46da63]511
512 return 0;
[80649a91]513}
514
[f2f0392]515/** Create a new fibril for a new connection.
[80649a91]516 *
[c07544d3]517 * Create new fibril for connection, fill in connection structures and inserts
[f2f0392]518 * it into the hash table, so that later we can easily do routing of messages to
519 * particular fibrils.
[53ca318]520 *
[3c22f70]521 * @param in_task_hash Identification of the incoming connection.
[c07544d3]522 * @param in_phone_hash Identification of the incoming connection.
523 * @param callid Hash of the opening IPC_M_CONNECT_ME_TO call.
524 * If callid is zero, the connection was opened by
525 * accepting the IPC_M_CONNECT_TO_ME call and this function
526 * is called directly by the server.
527 * @param call Call data of the opening call.
528 * @param cfibril Fibril function that should be called upon opening the
529 * connection.
530 *
531 * @return New fibril id or NULL on failure.
[36c9234]532 *
[80649a91]533 */
[3c22f70]534fid_t async_new_connection(sysarg_t in_task_hash, sysarg_t in_phone_hash,
535 ipc_callid_t callid, ipc_call_t *call,
536 void (*cfibril)(ipc_callid_t, ipc_call_t *))
[80649a91]537{
[c07544d3]538 connection_t *conn = malloc(sizeof(*conn));
[80649a91]539 if (!conn) {
[6675c70]540 if (callid)
[b74959bd]541 ipc_answer_0(callid, ENOMEM);
[0b4a67a]542 return (uintptr_t) NULL;
[80649a91]543 }
[c07544d3]544
[3c22f70]545 conn->in_task_hash = in_task_hash;
[44c6d88d]546 conn->in_phone_hash = in_phone_hash;
[80649a91]547 list_initialize(&conn->msg_queue);
548 conn->callid = callid;
[c4702804]549 conn->close_callid = 0;
[c07544d3]550
[eaf34f7]551 if (call)
552 conn->call = *call;
[6b21292]553
[c07544d3]554 /* We will activate the fibril ASAP */
555 conn->wdata.active = true;
556 conn->cfibril = cfibril;
[bc1f1c2]557 conn->wdata.fid = fibril_create(connection_fibril, conn);
[c07544d3]558
[bc1f1c2]559 if (!conn->wdata.fid) {
[80649a91]560 free(conn);
[6675c70]561 if (callid)
[b74959bd]562 ipc_answer_0(callid, ENOMEM);
[0b4a67a]563 return (uintptr_t) NULL;
[80649a91]564 }
[6b21292]565
[36c9234]566 /* Add connection to the connection hash table */
[9db9b10]567 unsigned long key = conn->in_phone_hash;
[c07544d3]568
[01ff41c]569 futex_down(&async_futex);
[80649a91]570 hash_table_insert(&conn_hash_table, &key, &conn->link);
[01ff41c]571 futex_up(&async_futex);
[6b21292]572
[bc1f1c2]573 fibril_add_ready(conn->wdata.fid);
[6b21292]574
[bc1f1c2]575 return conn->wdata.fid;
[80649a91]576}
577
[36c9234]578/** Handle a call that was received.
579 *
580 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
581 * Otherwise the call is routed to its connection fibril.
582 *
[c07544d3]583 * @param callid Hash of the incoming call.
584 * @param call Data of the incoming call.
[6b21292]585 *
[36c9234]586 */
[80649a91]587static void handle_call(ipc_callid_t callid, ipc_call_t *call)
588{
[44c6d88d]589 /* Unrouted call - do some default behaviour */
[15039b67]590 if ((callid & IPC_CALLID_NOTIFICATION)) {
[c07544d3]591 process_notification(callid, call);
[9db9b10]592 goto out;
[6b21292]593 }
594
[228e490]595 switch (IPC_GET_IMETHOD(*call)) {
[2c0e5d2]596 case IPC_M_CONNECT_ME:
[80649a91]597 case IPC_M_CONNECT_ME_TO:
[f2f0392]598 /* Open new connection with fibril etc. */
[3c22f70]599 async_new_connection(call->in_task_hash, IPC_GET_ARG5(*call),
600 callid, call, client_connection);
[9db9b10]601 goto out;
[80649a91]602 }
[6b21292]603
[36c9234]604 /* Try to route the call through the connection hash table */
[44c6d88d]605 if (route_call(callid, call))
[9db9b10]606 goto out;
[6b21292]607
[44c6d88d]608 /* Unknown call from unknown phone - hang it up */
[b74959bd]609 ipc_answer_0(callid, EHANGUP);
[9db9b10]610 return;
611
612out:
[953769f]613 ;
[450cd3a]614}
615
[f2f0392]616/** Fire all timeouts that expired. */
[c042bdd]617static void handle_expired_timeouts(void)
618{
619 struct timeval tv;
[36c9234]620 gettimeofday(&tv, NULL);
[c07544d3]621
[c042bdd]622 futex_down(&async_futex);
[c07544d3]623
624 link_t *cur = timeout_list.next;
[c042bdd]625 while (cur != &timeout_list) {
[f53cc81]626 awaiter_t *waiter;
[c07544d3]627
[f53cc81]628 waiter = list_get_instance(cur, awaiter_t, to_event.link);
629 if (tv_gt(&waiter->to_event.expires, &tv))
[c042bdd]630 break;
[f53cc81]631
[c042bdd]632 cur = cur->next;
[f53cc81]633
634 list_remove(&waiter->to_event.link);
635 waiter->to_event.inlist = false;
636 waiter->to_event.occurred = true;
[c07544d3]637
[36c9234]638 /*
[c07544d3]639 * Redundant condition?
640 * The fibril should not be active when it gets here.
[c042bdd]641 */
[49d072e]642 if (!waiter->active) {
[c07544d3]643 waiter->active = true;
[bc1f1c2]644 fibril_add_ready(waiter->fid);
[c042bdd]645 }
646 }
[c07544d3]647
[c042bdd]648 futex_up(&async_futex);
649}
650
[36c9234]651/** Endless loop dispatching incoming calls and answers.
652 *
[c07544d3]653 * @return Never returns.
654 *
[36c9234]655 */
[085bd54]656static int async_manager_worker(void)
[80649a91]657{
[c07544d3]658 while (true) {
[116d3f6f]659 if (fibril_switch(FIBRIL_FROM_MANAGER)) {
[a46da63]660 futex_up(&async_futex);
[36c9234]661 /*
662 * async_futex is always held when entering a manager
663 * fibril.
[a46da63]664 */
[80649a91]665 continue;
666 }
[c07544d3]667
[c042bdd]668 futex_down(&async_futex);
[c07544d3]669
670 suseconds_t timeout;
[c042bdd]671 if (!list_empty(&timeout_list)) {
[cc27c8c5]672 awaiter_t *waiter = list_get_instance(timeout_list.next,
[f53cc81]673 awaiter_t, to_event.link);
[c07544d3]674
675 struct timeval tv;
[bc1f1c2]676 gettimeofday(&tv, NULL);
[c07544d3]677
[f53cc81]678 if (tv_gteq(&tv, &waiter->to_event.expires)) {
[6c46350]679 futex_up(&async_futex);
[c042bdd]680 handle_expired_timeouts();
681 continue;
682 } else
[f53cc81]683 timeout = tv_sub(&waiter->to_event.expires,
684 &tv);
[c042bdd]685 } else
[0b99e40]686 timeout = SYNCH_NO_TIMEOUT;
[c07544d3]687
[c042bdd]688 futex_up(&async_futex);
[8619f25]689
690 atomic_inc(&threads_in_ipc_wait);
[c07544d3]691
692 ipc_call_t call;
[cc27c8c5]693 ipc_callid_t callid = ipc_wait_cycle(&call, timeout,
694 SYNCH_FLAGS_NONE);
[c07544d3]695
[8619f25]696 atomic_dec(&threads_in_ipc_wait);
697
[0b99e40]698 if (!callid) {
[c042bdd]699 handle_expired_timeouts();
[0b99e40]700 continue;
701 }
[c07544d3]702
703 if (callid & IPC_CALLID_ANSWERED)
[80649a91]704 continue;
[c07544d3]705
[80649a91]706 handle_call(callid, &call);
707 }
[a46da63]708
709 return 0;
[80649a91]710}
711
[36c9234]712/** Function to start async_manager as a standalone fibril.
[c07544d3]713 *
[36c9234]714 * When more kernel threads are used, one async manager should exist per thread.
715 *
[c07544d3]716 * @param arg Unused.
717 * @return Never returns.
[36c9234]718 *
[a2cd194]719 */
[9591265]720static int async_manager_fibril(void *arg)
[80649a91]721{
[a46da63]722 futex_up(&async_futex);
[c07544d3]723
[36c9234]724 /*
725 * async_futex is always locked when entering manager
726 */
[085bd54]727 async_manager_worker();
[a46da63]728
729 return 0;
[80649a91]730}
[450cd3a]731
[36c9234]732/** Add one manager to manager list. */
[80649a91]733void async_create_manager(void)
[450cd3a]734{
[c07544d3]735 fid_t fid = fibril_create(async_manager_fibril, NULL);
[bc1f1c2]736 fibril_add_manager(fid);
[80649a91]737}
738
739/** Remove one manager from manager list */
740void async_destroy_manager(void)
741{
[bc1f1c2]742 fibril_remove_manager();
[80649a91]743}
744
[36c9234]745/** Initialize the async framework.
746 *
[c07544d3]747 * @return Zero on success or an error code.
[36c9234]748 */
[db24058]749int __async_init(void)
[80649a91]750{
[bc1f1c2]751 if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
752 &conn_hash_table_ops)) {
[e35bf88]753 printf("%s: Cannot create async hash table\n", "libc");
[80649a91]754 return ENOMEM;
755 }
[c1c0184]756
757 _async_sess_init();
[80649a91]758
[a46da63]759 return 0;
[450cd3a]760}
[01ff41c]761
[36c9234]762/** Reply received callback.
[01ff41c]763 *
[36c9234]764 * This function is called whenever a reply for an asynchronous message sent out
765 * by the asynchronous framework is received.
766 *
767 * Notify the fibril which is waiting for this message that it has arrived.
768 *
[c07544d3]769 * @param arg Pointer to the asynchronous message record.
770 * @param retval Value returned in the answer.
771 * @param data Call data of the answer.
[01ff41c]772 */
[c07544d3]773static void reply_received(void *arg, int retval, ipc_call_t *data)
[01ff41c]774{
[9db9b10]775 futex_down(&async_futex);
776
[c07544d3]777 amsg_t *msg = (amsg_t *) arg;
[01ff41c]778 msg->retval = retval;
[c07544d3]779
[36c9234]780 /* Copy data after futex_down, just in case the call was detached */
[9db9b10]781 if ((msg->dataptr) && (data))
[c07544d3]782 *msg->dataptr = *data;
783
[c042bdd]784 write_barrier();
[c07544d3]785
[c042bdd]786 /* Remove message from timeout list */
[f53cc81]787 if (msg->wdata.to_event.inlist)
788 list_remove(&msg->wdata.to_event.link);
[c07544d3]789
790 msg->done = true;
[36c9234]791 if (!msg->wdata.active) {
[c07544d3]792 msg->wdata.active = true;
[bc1f1c2]793 fibril_add_ready(msg->wdata.fid);
[01ff41c]794 }
[c07544d3]795
[01ff41c]796 futex_up(&async_futex);
797}
798
[36c9234]799/** Send message and return id of the sent message.
800 *
801 * The return value can be used as input for async_wait() to wait for
802 * completion.
[01ff41c]803 *
[c07544d3]804 * @param phoneid Handle of the phone that will be used for the send.
805 * @param method Service-defined method.
806 * @param arg1 Service-defined payload argument.
807 * @param arg2 Service-defined payload argument.
808 * @param arg3 Service-defined payload argument.
809 * @param arg4 Service-defined payload argument.
810 * @param dataptr If non-NULL, storage where the reply data will be
811 * stored.
812 *
813 * @return Hash of the sent message or 0 on error.
[36c9234]814 *
[01ff41c]815 */
[96b02eb9]816aid_t async_send_fast(int phoneid, sysarg_t method, sysarg_t arg1,
817 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
[01ff41c]818{
[c07544d3]819 amsg_t *msg = malloc(sizeof(*msg));
820
821 if (!msg)
822 return 0;
[6b21292]823
[c07544d3]824 msg->done = false;
[01ff41c]825 msg->dataptr = dataptr;
[6b21292]826
[f53cc81]827 msg->wdata.to_event.inlist = false;
[36c9234]828 /* We may sleep in the next method, but it will use its own mechanism */
[c07544d3]829 msg->wdata.active = true;
830
[0cc4313]831 ipc_call_async_4(phoneid, method, arg1, arg2, arg3, arg4, msg,
[c07544d3]832 reply_received, true);
[6b21292]833
[01ff41c]834 return (aid_t) msg;
835}
836
[90f5d64]837/** Send message and return id of the sent message
838 *
[36c9234]839 * The return value can be used as input for async_wait() to wait for
840 * completion.
841 *
[c07544d3]842 * @param phoneid Handle of the phone that will be used for the send.
843 * @param method Service-defined method.
844 * @param arg1 Service-defined payload argument.
845 * @param arg2 Service-defined payload argument.
846 * @param arg3 Service-defined payload argument.
847 * @param arg4 Service-defined payload argument.
848 * @param arg5 Service-defined payload argument.
849 * @param dataptr If non-NULL, storage where the reply data will be
850 * stored.
851 *
852 * @return Hash of the sent message or 0 on error.
[36c9234]853 *
[90f5d64]854 */
[96b02eb9]855aid_t async_send_slow(int phoneid, sysarg_t method, sysarg_t arg1,
856 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
[0cc4313]857 ipc_call_t *dataptr)
[90f5d64]858{
[c07544d3]859 amsg_t *msg = malloc(sizeof(*msg));
[6b21292]860
[c07544d3]861 if (!msg)
862 return 0;
863
864 msg->done = false;
[90f5d64]865 msg->dataptr = dataptr;
[6b21292]866
[f53cc81]867 msg->wdata.to_event.inlist = false;
[36c9234]868 /* We may sleep in next method, but it will use its own mechanism */
[c07544d3]869 msg->wdata.active = true;
[6b21292]870
[0cc4313]871 ipc_call_async_5(phoneid, method, arg1, arg2, arg3, arg4, arg5, msg,
[c07544d3]872 reply_received, true);
[6b21292]873
[90f5d64]874 return (aid_t) msg;
875}
876
[36c9234]877/** Wait for a message sent by the async framework.
[01ff41c]878 *
[c07544d3]879 * @param amsgid Hash of the message to wait for.
880 * @param retval Pointer to storage where the retval of the answer will
881 * be stored.
882 *
[01ff41c]883 */
[96b02eb9]884void async_wait_for(aid_t amsgid, sysarg_t *retval)
[01ff41c]885{
886 amsg_t *msg = (amsg_t *) amsgid;
[c07544d3]887
[01ff41c]888 futex_down(&async_futex);
889 if (msg->done) {
890 futex_up(&async_futex);
891 goto done;
892 }
[c07544d3]893
[bc1f1c2]894 msg->wdata.fid = fibril_get_id();
[c07544d3]895 msg->wdata.active = false;
[f53cc81]896 msg->wdata.to_event.inlist = false;
[c07544d3]897
[36c9234]898 /* Leave the async_futex locked when entering this function */
[116d3f6f]899 fibril_switch(FIBRIL_TO_MANAGER);
[c07544d3]900
901 /* Futex is up automatically after fibril_switch */
902
[01ff41c]903done:
904 if (retval)
905 *retval = msg->retval;
[c07544d3]906
[01ff41c]907 free(msg);
908}
[0b99e40]909
[36c9234]910/** Wait for a message sent by the async framework, timeout variant.
[c042bdd]911 *
[c07544d3]912 * @param amsgid Hash of the message to wait for.
913 * @param retval Pointer to storage where the retval of the answer will
914 * be stored.
915 * @param timeout Timeout in microseconds.
916 *
917 * @return Zero on success, ETIMEOUT if the timeout has expired.
[c042bdd]918 *
919 */
[96b02eb9]920int async_wait_timeout(aid_t amsgid, sysarg_t *retval, suseconds_t timeout)
[c042bdd]921{
922 amsg_t *msg = (amsg_t *) amsgid;
[c07544d3]923
[86029498]924 /* TODO: Let it go through the event read at least once */
925 if (timeout < 0)
926 return ETIMEOUT;
[c07544d3]927
[c042bdd]928 futex_down(&async_futex);
929 if (msg->done) {
930 futex_up(&async_futex);
931 goto done;
932 }
[c07544d3]933
[f53cc81]934 gettimeofday(&msg->wdata.to_event.expires, NULL);
935 tv_add(&msg->wdata.to_event.expires, timeout);
[c07544d3]936
[bc1f1c2]937 msg->wdata.fid = fibril_get_id();
[c07544d3]938 msg->wdata.active = false;
[b6ee5b1]939 async_insert_timeout(&msg->wdata);
[c07544d3]940
[36c9234]941 /* Leave the async_futex locked when entering this function */
[116d3f6f]942 fibril_switch(FIBRIL_TO_MANAGER);
[c07544d3]943
944 /* Futex is up automatically after fibril_switch */
945
[c042bdd]946 if (!msg->done)
947 return ETIMEOUT;
[c07544d3]948
[c042bdd]949done:
950 if (retval)
951 *retval = msg->retval;
[c07544d3]952
[c042bdd]953 free(msg);
[c07544d3]954
[c042bdd]955 return 0;
956}
[0b99e40]957
[36c9234]958/** Wait for specified time.
[44c6d88d]959 *
[36c9234]960 * The current fibril is suspended but the thread continues to execute.
961 *
[c07544d3]962 * @param timeout Duration of the wait in microseconds.
963 *
[44c6d88d]964 */
965void async_usleep(suseconds_t timeout)
966{
[c07544d3]967 amsg_t *msg = malloc(sizeof(*msg));
[44c6d88d]968
969 if (!msg)
970 return;
[6b21292]971
[bc1f1c2]972 msg->wdata.fid = fibril_get_id();
[c07544d3]973 msg->wdata.active = false;
[6b21292]974
[f53cc81]975 gettimeofday(&msg->wdata.to_event.expires, NULL);
976 tv_add(&msg->wdata.to_event.expires, timeout);
[6b21292]977
[44c6d88d]978 futex_down(&async_futex);
[c07544d3]979
[b6ee5b1]980 async_insert_timeout(&msg->wdata);
[c07544d3]981
[36c9234]982 /* Leave the async_futex locked when entering this function */
[116d3f6f]983 fibril_switch(FIBRIL_TO_MANAGER);
[c07544d3]984
985 /* Futex is up automatically after fibril_switch() */
986
[44c6d88d]987 free(msg);
988}
[da0c91e7]989
[36c9234]990/** Setter for client_connection function pointer.
[da0c91e7]991 *
[c07544d3]992 * @param conn Function that will implement a new connection fibril.
993 *
[da0c91e7]994 */
995void async_set_client_connection(async_client_conn_t conn)
996{
997 client_connection = conn;
998}
[36c9234]999
1000/** Setter for interrupt_received function pointer.
1001 *
[c07544d3]1002 * @param intr Function that will implement a new interrupt
1003 * notification fibril.
[36c9234]1004 */
[c07544d3]1005void async_set_interrupt_received(async_client_conn_t intr)
[51dbadf3]1006{
[c07544d3]1007 interrupt_received = intr;
[51dbadf3]1008}
[085bd54]1009
[0cc4313]1010/** Pseudo-synchronous message sending - fast version.
1011 *
1012 * Send message asynchronously and return only after the reply arrives.
1013 *
1014 * This function can only transfer 4 register payload arguments. For
1015 * transferring more arguments, see the slower async_req_slow().
1016 *
[c07544d3]1017 * @param phoneid Hash of the phone through which to make the call.
1018 * @param method Method of the call.
1019 * @param arg1 Service-defined payload argument.
1020 * @param arg2 Service-defined payload argument.
1021 * @param arg3 Service-defined payload argument.
1022 * @param arg4 Service-defined payload argument.
1023 * @param r1 If non-NULL, storage for the 1st reply argument.
1024 * @param r2 If non-NULL, storage for the 2nd reply argument.
1025 * @param r3 If non-NULL, storage for the 3rd reply argument.
1026 * @param r4 If non-NULL, storage for the 4th reply argument.
1027 * @param r5 If non-NULL, storage for the 5th reply argument.
1028 *
1029 * @return Return code of the reply or a negative error code.
1030 *
[0cc4313]1031 */
[96b02eb9]1032sysarg_t async_req_fast(int phoneid, sysarg_t method, sysarg_t arg1,
1033 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
1034 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
[085bd54]1035{
[0cc4313]1036 ipc_call_t result;
1037 aid_t eid = async_send_4(phoneid, method, arg1, arg2, arg3, arg4,
1038 &result);
[c07544d3]1039
[96b02eb9]1040 sysarg_t rc;
[0cc4313]1041 async_wait_for(eid, &rc);
[c07544d3]1042
1043 if (r1)
[0cc4313]1044 *r1 = IPC_GET_ARG1(result);
[c07544d3]1045
[0cc4313]1046 if (r2)
1047 *r2 = IPC_GET_ARG2(result);
[c07544d3]1048
[0cc4313]1049 if (r3)
1050 *r3 = IPC_GET_ARG3(result);
[c07544d3]1051
[0cc4313]1052 if (r4)
1053 *r4 = IPC_GET_ARG4(result);
[c07544d3]1054
[0cc4313]1055 if (r5)
1056 *r5 = IPC_GET_ARG5(result);
[c07544d3]1057
[0cc4313]1058 return rc;
[085bd54]1059}
1060
[0cc4313]1061/** Pseudo-synchronous message sending - slow version.
1062 *
1063 * Send message asynchronously and return only after the reply arrives.
1064 *
[c07544d3]1065 * @param phoneid Hash of the phone through which to make the call.
1066 * @param method Method of the call.
1067 * @param arg1 Service-defined payload argument.
1068 * @param arg2 Service-defined payload argument.
1069 * @param arg3 Service-defined payload argument.
1070 * @param arg4 Service-defined payload argument.
1071 * @param arg5 Service-defined payload argument.
1072 * @param r1 If non-NULL, storage for the 1st reply argument.
1073 * @param r2 If non-NULL, storage for the 2nd reply argument.
1074 * @param r3 If non-NULL, storage for the 3rd reply argument.
1075 * @param r4 If non-NULL, storage for the 4th reply argument.
1076 * @param r5 If non-NULL, storage for the 5th reply argument.
1077 *
1078 * @return Return code of the reply or a negative error code.
1079 *
[0cc4313]1080 */
[96b02eb9]1081sysarg_t async_req_slow(int phoneid, sysarg_t method, sysarg_t arg1,
1082 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
1083 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
[085bd54]1084{
[0cc4313]1085 ipc_call_t result;
1086 aid_t eid = async_send_5(phoneid, method, arg1, arg2, arg3, arg4, arg5,
1087 &result);
[c07544d3]1088
[96b02eb9]1089 sysarg_t rc;
[0cc4313]1090 async_wait_for(eid, &rc);
[c07544d3]1091
1092 if (r1)
[0cc4313]1093 *r1 = IPC_GET_ARG1(result);
[c07544d3]1094
[0cc4313]1095 if (r2)
1096 *r2 = IPC_GET_ARG2(result);
[c07544d3]1097
[0cc4313]1098 if (r3)
1099 *r3 = IPC_GET_ARG3(result);
[c07544d3]1100
[0cc4313]1101 if (r4)
1102 *r4 = IPC_GET_ARG4(result);
[c07544d3]1103
[0cc4313]1104 if (r5)
1105 *r5 = IPC_GET_ARG5(result);
[c07544d3]1106
[0cc4313]1107 return rc;
[085bd54]1108}
[b2951e2]1109
[f74392f]1110/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
1111 *
1112 * Ask through phone for a new connection to some service.
1113 *
1114 * @param phoneid Phone handle used for contacting the other side.
1115 * @param arg1 User defined argument.
1116 * @param arg2 User defined argument.
1117 * @param arg3 User defined argument.
1118 *
1119 * @return New phone handle on success or a negative error code.
1120 */
1121int
[96b02eb9]1122async_connect_me_to(int phoneid, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
[f74392f]1123{
1124 int rc;
[96b02eb9]1125 sysarg_t newphid;
[f74392f]1126
1127 rc = async_req_3_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, NULL,
1128 NULL, NULL, NULL, &newphid);
1129
1130 if (rc != EOK)
1131 return rc;
1132
1133 return newphid;
1134}
1135
1136/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
1137 *
1138 * Ask through phone for a new connection to some service and block until
1139 * success.
1140 *
1141 * @param phoneid Phone handle used for contacting the other side.
1142 * @param arg1 User defined argument.
1143 * @param arg2 User defined argument.
1144 * @param arg3 User defined argument.
1145 *
1146 * @return New phone handle on success or a negative error code.
1147 */
1148int
[96b02eb9]1149async_connect_me_to_blocking(int phoneid, sysarg_t arg1, sysarg_t arg2,
1150 sysarg_t arg3)
[f74392f]1151{
1152 int rc;
[96b02eb9]1153 sysarg_t newphid;
[f74392f]1154
1155 rc = async_req_4_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3,
1156 IPC_FLAG_BLOCKING, NULL, NULL, NULL, NULL, &newphid);
1157
1158 if (rc != EOK)
1159 return rc;
1160
1161 return newphid;
1162}
1163
[0da4e41]1164/** Wrapper for making IPC_M_SHARE_IN calls using the async framework.
1165 *
1166 * @param phoneid Phone that will be used to contact the receiving side.
1167 * @param dst Destination address space area base.
1168 * @param size Size of the destination address space area.
1169 * @param arg User defined argument.
1170 * @param flags Storage where the received flags will be stored. Can be
1171 * NULL.
1172 *
1173 * @return Zero on success or a negative error code from errno.h.
1174 */
[96b02eb9]1175int async_share_in_start(int phoneid, void *dst, size_t size, sysarg_t arg,
[0da4e41]1176 int *flags)
1177{
1178 int res;
1179 sysarg_t tmp_flags;
[96b02eb9]1180 res = async_req_3_2(phoneid, IPC_M_SHARE_IN, (sysarg_t) dst,
1181 (sysarg_t) size, arg, NULL, &tmp_flags);
[0da4e41]1182 if (flags)
1183 *flags = tmp_flags;
1184 return res;
1185}
1186
1187/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
1188 *
1189 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN calls
1190 * so that the user doesn't have to remember the meaning of each IPC argument.
1191 *
1192 * So far, this wrapper is to be used from within a connection fibril.
1193 *
1194 * @param callid Storage where the hash of the IPC_M_SHARE_IN call will
1195 * be stored.
1196 * @param size Destination address space area size.
1197 *
1198 * @return Non-zero on success, zero on failure.
1199 */
1200int async_share_in_receive(ipc_callid_t *callid, size_t *size)
1201{
1202 ipc_call_t data;
1203
1204 assert(callid);
1205 assert(size);
1206
1207 *callid = async_get_call(&data);
[228e490]1208 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
[0da4e41]1209 return 0;
1210 *size = (size_t) IPC_GET_ARG2(data);
1211 return 1;
1212}
1213
1214/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
1215 *
1216 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls
1217 * so that the user doesn't have to remember the meaning of each IPC argument.
1218 *
1219 * @param callid Hash of the IPC_M_DATA_READ call to answer.
1220 * @param src Source address space base.
1221 * @param flags Flags to be used for sharing. Bits can be only cleared.
1222 *
1223 * @return Zero on success or a value from @ref errno.h on failure.
1224 */
1225int async_share_in_finalize(ipc_callid_t callid, void *src, int flags)
1226{
1227 return ipc_share_in_finalize(callid, src, flags);
1228}
1229
1230/** Wrapper for making IPC_M_SHARE_OUT calls using the async framework.
1231 *
1232 * @param phoneid Phone that will be used to contact the receiving side.
1233 * @param src Source address space area base address.
1234 * @param flags Flags to be used for sharing. Bits can be only cleared.
1235 *
1236 * @return Zero on success or a negative error code from errno.h.
1237 */
1238int async_share_out_start(int phoneid, void *src, int flags)
1239{
[96b02eb9]1240 return async_req_3_0(phoneid, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
1241 (sysarg_t) flags);
[0da4e41]1242}
1243
1244/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
1245 *
1246 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT calls
1247 * so that the user doesn't have to remember the meaning of each IPC argument.
1248 *
1249 * So far, this wrapper is to be used from within a connection fibril.
1250 *
1251 * @param callid Storage where the hash of the IPC_M_SHARE_OUT call will
1252 * be stored.
1253 * @param size Storage where the source address space area size will be
1254 * stored.
1255 * @param flags Storage where the sharing flags will be stored.
1256 *
1257 * @return Non-zero on success, zero on failure.
1258 */
1259int async_share_out_receive(ipc_callid_t *callid, size_t *size, int *flags)
1260{
1261 ipc_call_t data;
1262
1263 assert(callid);
1264 assert(size);
1265 assert(flags);
1266
1267 *callid = async_get_call(&data);
[228e490]1268 if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
[0da4e41]1269 return 0;
1270 *size = (size_t) IPC_GET_ARG2(data);
1271 *flags = (int) IPC_GET_ARG3(data);
1272 return 1;
1273}
1274
1275/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
1276 *
1277 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT calls
1278 * so that the user doesn't have to remember the meaning of each IPC argument.
1279 *
1280 * @param callid Hash of the IPC_M_DATA_WRITE call to answer.
1281 * @param dst Destination address space area base address.
1282 *
1283 * @return Zero on success or a value from @ref errno.h on failure.
1284 */
1285int async_share_out_finalize(ipc_callid_t callid, void *dst)
1286{
1287 return ipc_share_out_finalize(callid, dst);
1288}
1289
1290
1291/** Wrapper for making IPC_M_DATA_READ calls using the async framework.
1292 *
1293 * @param phoneid Phone that will be used to contact the receiving side.
1294 * @param dst Address of the beginning of the destination buffer.
1295 * @param size Size of the destination buffer.
1296 *
1297 * @return Zero on success or a negative error code from errno.h.
1298 */
1299int async_data_read_start(int phoneid, void *dst, size_t size)
1300{
[96b02eb9]1301 return async_req_2_0(phoneid, IPC_M_DATA_READ, (sysarg_t) dst,
1302 (sysarg_t) size);
[0da4e41]1303}
1304
1305/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1306 *
1307 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ calls
1308 * so that the user doesn't have to remember the meaning of each IPC argument.
1309 *
1310 * So far, this wrapper is to be used from within a connection fibril.
1311 *
1312 * @param callid Storage where the hash of the IPC_M_DATA_READ call will
1313 * be stored.
1314 * @param size Storage where the maximum size will be stored. Can be
1315 * NULL.
1316 *
1317 * @return Non-zero on success, zero on failure.
1318 */
1319int async_data_read_receive(ipc_callid_t *callid, size_t *size)
1320{
1321 ipc_call_t data;
1322
1323 assert(callid);
1324
1325 *callid = async_get_call(&data);
[228e490]1326 if (IPC_GET_IMETHOD(data) != IPC_M_DATA_READ)
[0da4e41]1327 return 0;
1328 if (size)
1329 *size = (size_t) IPC_GET_ARG2(data);
1330 return 1;
1331}
1332
1333/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
1334 *
1335 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls
1336 * so that the user doesn't have to remember the meaning of each IPC argument.
1337 *
1338 * @param callid Hash of the IPC_M_DATA_READ call to answer.
1339 * @param src Source address for the IPC_M_DATA_READ call.
1340 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
1341 * the maximum size announced by the sender.
1342 *
1343 * @return Zero on success or a value from @ref errno.h on failure.
1344 */
1345int async_data_read_finalize(ipc_callid_t callid, const void *src, size_t size)
1346{
1347 return ipc_data_read_finalize(callid, src, size);
1348}
1349
[b4cbef1]1350/** Wrapper for forwarding any read request
1351 *
1352 *
1353 */
[96b02eb9]1354int async_data_read_forward_fast(int phoneid, sysarg_t method, sysarg_t arg1,
1355 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
[b4cbef1]1356{
1357 ipc_callid_t callid;
1358 if (!async_data_read_receive(&callid, NULL)) {
1359 ipc_answer_0(callid, EINVAL);
1360 return EINVAL;
1361 }
1362
1363 aid_t msg = async_send_fast(phoneid, method, arg1, arg2, arg3, arg4,
1364 dataptr);
1365 if (msg == 0) {
1366 ipc_answer_0(callid, EINVAL);
1367 return EINVAL;
1368 }
1369
1370 int retval = ipc_forward_fast(callid, phoneid, 0, 0, 0,
1371 IPC_FF_ROUTE_FROM_ME);
1372 if (retval != EOK) {
[a281fc82]1373 async_wait_for(msg, NULL);
[b4cbef1]1374 ipc_answer_0(callid, retval);
1375 return retval;
1376 }
1377
[96b02eb9]1378 sysarg_t rc;
[b4cbef1]1379 async_wait_for(msg, &rc);
1380
1381 return (int) rc;
1382}
1383
[0da4e41]1384/** Wrapper for making IPC_M_DATA_WRITE calls using the async framework.
1385 *
[b4cbef1]1386 * @param phoneid Phone that will be used to contact the receiving side.
1387 * @param src Address of the beginning of the source buffer.
1388 * @param size Size of the source buffer.
1389 *
1390 * @return Zero on success or a negative error code from errno.h.
[0da4e41]1391 *
1392 */
1393int async_data_write_start(int phoneid, const void *src, size_t size)
1394{
[96b02eb9]1395 return async_req_2_0(phoneid, IPC_M_DATA_WRITE, (sysarg_t) src,
1396 (sysarg_t) size);
[0da4e41]1397}
1398
1399/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1400 *
1401 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE calls
1402 * so that the user doesn't have to remember the meaning of each IPC argument.
1403 *
1404 * So far, this wrapper is to be used from within a connection fibril.
1405 *
[b4cbef1]1406 * @param callid Storage where the hash of the IPC_M_DATA_WRITE call will
1407 * be stored.
1408 * @param size Storage where the suggested size will be stored. May be
1409 * NULL
1410 *
1411 * @return Non-zero on success, zero on failure.
[0da4e41]1412 *
1413 */
1414int async_data_write_receive(ipc_callid_t *callid, size_t *size)
1415{
1416 ipc_call_t data;
1417
1418 assert(callid);
[b4cbef1]1419
[0da4e41]1420 *callid = async_get_call(&data);
[228e490]1421 if (IPC_GET_IMETHOD(data) != IPC_M_DATA_WRITE)
[0da4e41]1422 return 0;
[b4cbef1]1423
[0da4e41]1424 if (size)
1425 *size = (size_t) IPC_GET_ARG2(data);
[b4cbef1]1426
[0da4e41]1427 return 1;
1428}
1429
1430/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
1431 *
1432 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE calls
1433 * so that the user doesn't have to remember the meaning of each IPC argument.
1434 *
[b4cbef1]1435 * @param callid Hash of the IPC_M_DATA_WRITE call to answer.
1436 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
1437 * @param size Final size for the IPC_M_DATA_WRITE call.
1438 *
1439 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1440 *
1441 */
1442int async_data_write_finalize(ipc_callid_t callid, void *dst, size_t size)
1443{
1444 return ipc_data_write_finalize(callid, dst, size);
1445}
1446
[eda925a]1447/** Wrapper for receiving binary data or strings
[8aa42e3]1448 *
1449 * This wrapper only makes it more comfortable to use async_data_write_*
[eda925a]1450 * functions to receive binary data or strings.
[8aa42e3]1451 *
[472c09d]1452 * @param data Pointer to data pointer (which should be later disposed
1453 * by free()). If the operation fails, the pointer is not
1454 * touched.
[eda925a]1455 * @param nullterm If true then the received data is always zero terminated.
1456 * This also causes to allocate one extra byte beyond the
1457 * raw transmitted data.
[b4cbef1]1458 * @param min_size Minimum size (in bytes) of the data to receive.
[472c09d]1459 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
1460 * no limit.
[eda925a]1461 * @param granulariy If non-zero then the size of the received data has to
[472c09d]1462 * be divisible by this value.
1463 * @param received If not NULL, the size of the received data is stored here.
[8aa42e3]1464 *
1465 * @return Zero on success or a value from @ref errno.h on failure.
1466 *
1467 */
[eda925a]1468int async_data_write_accept(void **data, const bool nullterm,
1469 const size_t min_size, const size_t max_size, const size_t granularity,
1470 size_t *received)
[8aa42e3]1471{
1472 ipc_callid_t callid;
1473 size_t size;
1474 if (!async_data_write_receive(&callid, &size)) {
1475 ipc_answer_0(callid, EINVAL);
1476 return EINVAL;
1477 }
1478
[b4cbef1]1479 if (size < min_size) {
1480 ipc_answer_0(callid, EINVAL);
1481 return EINVAL;
1482 }
1483
[8aa42e3]1484 if ((max_size > 0) && (size > max_size)) {
1485 ipc_answer_0(callid, EINVAL);
1486 return EINVAL;
1487 }
1488
[472c09d]1489 if ((granularity > 0) && ((size % granularity) != 0)) {
1490 ipc_answer_0(callid, EINVAL);
1491 return EINVAL;
1492 }
1493
[eda925a]1494 void *_data;
1495
1496 if (nullterm)
1497 _data = malloc(size + 1);
1498 else
1499 _data = malloc(size);
1500
[472c09d]1501 if (_data == NULL) {
[8aa42e3]1502 ipc_answer_0(callid, ENOMEM);
1503 return ENOMEM;
1504 }
1505
[472c09d]1506 int rc = async_data_write_finalize(callid, _data, size);
[8aa42e3]1507 if (rc != EOK) {
[472c09d]1508 free(_data);
[8aa42e3]1509 return rc;
1510 }
1511
[eda925a]1512 if (nullterm)
1513 ((char *) _data)[size] = 0;
[8aa42e3]1514
[eda925a]1515 *data = _data;
[472c09d]1516 if (received != NULL)
1517 *received = size;
1518
[8aa42e3]1519 return EOK;
1520}
1521
[b4cbef1]1522/** Wrapper for voiding any data that is about to be received
1523 *
1524 * This wrapper can be used to void any pending data
1525 *
1526 * @param retval Error value from @ref errno.h to be returned to the caller.
1527 *
1528 */
[eda925a]1529void async_data_write_void(const int retval)
[b4cbef1]1530{
1531 ipc_callid_t callid;
1532 async_data_write_receive(&callid, NULL);
1533 ipc_answer_0(callid, retval);
1534}
1535
1536/** Wrapper for forwarding any data that is about to be received
1537 *
1538 *
1539 */
[96b02eb9]1540int async_data_write_forward_fast(int phoneid, sysarg_t method, sysarg_t arg1,
1541 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
[b4cbef1]1542{
1543 ipc_callid_t callid;
1544 if (!async_data_write_receive(&callid, NULL)) {
1545 ipc_answer_0(callid, EINVAL);
1546 return EINVAL;
1547 }
1548
1549 aid_t msg = async_send_fast(phoneid, method, arg1, arg2, arg3, arg4,
1550 dataptr);
1551 if (msg == 0) {
1552 ipc_answer_0(callid, EINVAL);
1553 return EINVAL;
1554 }
1555
1556 int retval = ipc_forward_fast(callid, phoneid, 0, 0, 0,
1557 IPC_FF_ROUTE_FROM_ME);
1558 if (retval != EOK) {
[a281fc82]1559 async_wait_for(msg, NULL);
[b4cbef1]1560 ipc_answer_0(callid, retval);
1561 return retval;
1562 }
1563
[96b02eb9]1564 sysarg_t rc;
[b4cbef1]1565 async_wait_for(msg, &rc);
1566
1567 return (int) rc;
1568}
1569
[a46da63]1570/** @}
[b2951e2]1571 */
Note: See TracBrowser for help on using the repository browser.