async.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2006 Ondrej Palkovsky
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00089 #define LIBC_ASYNC_C_
00090 #include <ipc/ipc.h>
00091 #include <async.h>
00092 #undef LIBC_ASYNC_C_
00093 
00094 #include <futex.h>
00095 #include <fibril.h>
00096 #include <stdio.h>
00097 #include <adt/hash_table.h>
00098 #include <adt/list.h>
00099 #include <assert.h>
00100 #include <errno.h>
00101 #include <sys/time.h>
00102 #include <arch/barrier.h>
00103 #include <bool.h>
00104 #include <stdlib.h>
00105 #include <malloc.h>
00106 #include "private/async.h"
00107 
00108 atomic_t async_futex = FUTEX_INITIALIZER;
00109 
00111 atomic_t threads_in_ipc_wait = { 0 };
00112 
00113 typedef struct {
00114         awaiter_t wdata;
00115         
00117         bool done;
00118         
00120         ipc_call_t *dataptr;
00121         
00122         sysarg_t retval;
00123 } amsg_t;
00124 
00129 typedef struct {
00130         link_t link;
00131         ipc_callid_t callid;
00132         ipc_call_t call;
00133 } msg_t;
00134 
00135 typedef struct {
00136         sysarg_t in_task_hash;
00137         link_t link;
00138         int refcnt;
00139         void *data;
00140 } client_t;
00141 
00142 typedef struct {
00143         awaiter_t wdata;
00144         
00146         link_t link;
00147         
00149         sysarg_t in_task_hash;
00151         sysarg_t in_phone_hash;
00152         
00154         client_t *client;
00155         
00157         link_t msg_queue;
00158         
00160         ipc_callid_t callid;
00162         ipc_call_t call;
00163         
00165         ipc_callid_t close_callid;
00166         
00168         void (*cfibril)(ipc_callid_t, ipc_call_t *);
00169 } connection_t;
00170 
00172 static fibril_local connection_t *FIBRIL_connection;
00173 
00174 static void *default_client_data_constructor(void)
00175 {
00176         return NULL;
00177 }
00178 
00179 static void default_client_data_destructor(void *data)
00180 {
00181 }
00182 
00183 static async_client_data_ctor_t async_client_data_create =
00184     default_client_data_constructor;
00185 static async_client_data_dtor_t async_client_data_destroy =
00186     default_client_data_destructor;
00187 
00188 void async_set_client_data_constructor(async_client_data_ctor_t ctor)
00189 {
00190         async_client_data_create = ctor;
00191 }
00192 
00193 void async_set_client_data_destructor(async_client_data_dtor_t dtor)
00194 {
00195         async_client_data_destroy = dtor;
00196 }
00197 
00198 void *async_client_data_get(void)
00199 {
00200         assert(FIBRIL_connection);
00201         return FIBRIL_connection->client->data;
00202 }
00203 
00212 static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)
00213 {
00214         ipc_answer_0(callid, ENOENT);
00215 }
00216 
00220 static async_client_conn_t client_connection = default_client_connection;
00221 
00230 static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
00231 {
00232 }
00233 
00238 static async_client_conn_t interrupt_received = default_interrupt_received;
00239 
00240 static hash_table_t client_hash_table;
00241 static hash_table_t conn_hash_table;
00242 static LIST_INITIALIZE(timeout_list);
00243 
00244 #define CLIENT_HASH_TABLE_BUCKETS  32
00245 #define CONN_HASH_TABLE_BUCKETS    32
00246 
00247 static hash_index_t client_hash(unsigned long key[])
00248 {
00249         assert(key);
00250         return (((key[0]) >> 4) % CLIENT_HASH_TABLE_BUCKETS);
00251 }
00252 
00253 static int client_compare(unsigned long key[], hash_count_t keys, link_t *item)
00254 {
00255         client_t *client = hash_table_get_instance(item, client_t, link);
00256         return (key[0] == client->in_task_hash);
00257 }
00258 
00259 static void client_remove(link_t *item)
00260 {
00261 }
00262 
00264 static hash_table_operations_t client_hash_table_ops = {
00265         .hash = client_hash,
00266         .compare = client_compare,
00267         .remove_callback = client_remove
00268 };
00269 
00277 static hash_index_t conn_hash(unsigned long key[])
00278 {
00279         assert(key);
00280         return (((key[0]) >> 4) % CONN_HASH_TABLE_BUCKETS);
00281 }
00282 
00292 static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
00293 {
00294         connection_t *conn = hash_table_get_instance(item, connection_t, link);
00295         return (key[0] == conn->in_phone_hash);
00296 }
00297 
00298 static void conn_remove(link_t *item)
00299 {
00300 }
00301 
00303 static hash_table_operations_t conn_hash_table_ops = {
00304         .hash = conn_hash,
00305         .compare = conn_compare,
00306         .remove_callback = conn_remove
00307 };
00308 
00314 void async_insert_timeout(awaiter_t *wd)
00315 {
00316         wd->to_event.occurred = false;
00317         wd->to_event.inlist = true;
00318         
00319         link_t *tmp = timeout_list.next;
00320         while (tmp != &timeout_list) {
00321                 awaiter_t *cur
00322                     = list_get_instance(tmp, awaiter_t, to_event.link);
00323                 
00324                 if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
00325                         break;
00326                 
00327                 tmp = tmp->next;
00328         }
00329         
00330         list_append(&wd->to_event.link, tmp);
00331 }
00332 
00346 static bool route_call(ipc_callid_t callid, ipc_call_t *call)
00347 {
00348         futex_down(&async_futex);
00349         
00350         unsigned long key = call->in_phone_hash;
00351         link_t *hlp = hash_table_find(&conn_hash_table, &key);
00352         
00353         if (!hlp) {
00354                 futex_up(&async_futex);
00355                 return false;
00356         }
00357         
00358         connection_t *conn = hash_table_get_instance(hlp, connection_t, link);
00359         
00360         msg_t *msg = malloc(sizeof(*msg));
00361         if (!msg) {
00362                 futex_up(&async_futex);
00363                 return false;
00364         }
00365         
00366         msg->callid = callid;
00367         msg->call = *call;
00368         list_append(&msg->link, &conn->msg_queue);
00369         
00370         if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
00371                 conn->close_callid = callid;
00372         
00373         /* If the connection fibril is waiting for an event, activate it */
00374         if (!conn->wdata.active) {
00375                 
00376                 /* If in timeout list, remove it */
00377                 if (conn->wdata.to_event.inlist) {
00378                         conn->wdata.to_event.inlist = false;
00379                         list_remove(&conn->wdata.to_event.link);
00380                 }
00381                 
00382                 conn->wdata.active = true;
00383                 fibril_add_ready(conn->wdata.fid);
00384         }
00385         
00386         futex_up(&async_futex);
00387         return true;
00388 }
00389 
00400 static int notification_fibril(void *arg)
00401 {
00402         msg_t *msg = (msg_t *) arg;
00403         interrupt_received(msg->callid, &msg->call);
00404         
00405         free(msg);
00406         return 0;
00407 }
00408 
00420 static bool process_notification(ipc_callid_t callid, ipc_call_t *call)
00421 {
00422         futex_down(&async_futex);
00423         
00424         msg_t *msg = malloc(sizeof(*msg));
00425         if (!msg) {
00426                 futex_up(&async_futex);
00427                 return false;
00428         }
00429         
00430         msg->callid = callid;
00431         msg->call = *call;
00432         
00433         fid_t fid = fibril_create(notification_fibril, msg);
00434         if (fid == 0) {
00435                 free(msg);
00436                 futex_up(&async_futex);
00437                 return false;
00438         }
00439         
00440         fibril_add_ready(fid);
00441         
00442         futex_up(&async_futex);
00443         return true;
00444 }
00445 
00458 ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
00459 {
00460         assert(FIBRIL_connection);
00461         
00462         /* Why doing this?
00463          * GCC 4.1.0 coughs on FIBRIL_connection-> dereference.
00464          * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
00465          *           I would never expect to find so many errors in
00466          *           a compiler.
00467          */
00468         connection_t *conn = FIBRIL_connection;
00469         
00470         futex_down(&async_futex);
00471         
00472         if (usecs) {
00473                 gettimeofday(&conn->wdata.to_event.expires, NULL);
00474                 tv_add(&conn->wdata.to_event.expires, usecs);
00475         } else
00476                 conn->wdata.to_event.inlist = false;
00477         
00478         /* If nothing in queue, wait until something arrives */
00479         while (list_empty(&conn->msg_queue)) {
00480                 if (conn->close_callid) {
00481                         /*
00482                          * Handle the case when the connection was already
00483                          * closed by the client but the server did not notice
00484                          * the first IPC_M_PHONE_HUNGUP call and continues to
00485                          * call async_get_call_timeout(). Repeat
00486                          * IPC_M_PHONE_HUNGUP until the caller notices.
00487                          */
00488                         memset(call, 0, sizeof(ipc_call_t));
00489                         IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
00490                         futex_up(&async_futex);
00491                         return conn->close_callid;
00492                 }
00493                 
00494                 if (usecs)
00495                         async_insert_timeout(&conn->wdata);
00496                 
00497                 conn->wdata.active = false;
00498                 
00499                 /*
00500                  * Note: the current fibril will be rescheduled either due to a
00501                  * timeout or due to an arriving message destined to it. In the
00502                  * former case, handle_expired_timeouts() and, in the latter
00503                  * case, route_call() will perform the wakeup.
00504                  */
00505                 fibril_switch(FIBRIL_TO_MANAGER);
00506                 
00507                 /*
00508                  * Futex is up after getting back from async_manager.
00509                  * Get it again.
00510                  */
00511                 futex_down(&async_futex);
00512                 if ((usecs) && (conn->wdata.to_event.occurred)
00513                     && (list_empty(&conn->msg_queue))) {
00514                         /* If we timed out -> exit */
00515                         futex_up(&async_futex);
00516                         return 0;
00517                 }
00518         }
00519         
00520         msg_t *msg = list_get_instance(conn->msg_queue.next, msg_t, link);
00521         list_remove(&msg->link);
00522         
00523         ipc_callid_t callid = msg->callid;
00524         *call = msg->call;
00525         free(msg);
00526         
00527         futex_up(&async_futex);
00528         return callid;
00529 }
00530 
00541 static int connection_fibril(void *arg)
00542 {
00543         /*
00544          * Setup fibril-local connection pointer.
00545          */
00546         FIBRIL_connection = (connection_t *) arg;
00547         
00548         futex_down(&async_futex);
00549         
00550         /*
00551          * Add our reference for the current connection in the client task
00552          * tracking structure. If this is the first reference, create and
00553          * hash in a new tracking structure.
00554          */
00555         
00556         unsigned long key = FIBRIL_connection->in_task_hash;
00557         link_t *lnk = hash_table_find(&client_hash_table, &key);
00558         
00559         client_t *client;
00560         
00561         if (lnk) {
00562                 client = hash_table_get_instance(lnk, client_t, link);
00563                 client->refcnt++;
00564         } else {
00565                 client = malloc(sizeof(client_t));
00566                 if (!client) {
00567                         ipc_answer_0(FIBRIL_connection->callid, ENOMEM);
00568                         futex_up(&async_futex);
00569                         return 0;
00570                 }
00571                 
00572                 client->in_task_hash = FIBRIL_connection->in_task_hash;
00573                 
00574                 async_serialize_start();
00575                 client->data = async_client_data_create();
00576                 async_serialize_end();
00577                 
00578                 client->refcnt = 1;
00579                 hash_table_insert(&client_hash_table, &key, &client->link);
00580         }
00581         
00582         futex_up(&async_futex);
00583         
00584         FIBRIL_connection->client = client;
00585         
00586         /*
00587          * Call the connection handler function.
00588          */
00589         FIBRIL_connection->cfibril(FIBRIL_connection->callid,
00590             &FIBRIL_connection->call);
00591         
00592         /*
00593          * Remove the reference for this client task connection.
00594          */
00595         bool destroy;
00596         
00597         futex_down(&async_futex);
00598         
00599         if (--client->refcnt == 0) {
00600                 hash_table_remove(&client_hash_table, &key, 1);
00601                 destroy = true;
00602         } else
00603                 destroy = false;
00604         
00605         futex_up(&async_futex);
00606         
00607         if (destroy) {
00608                 if (client->data)
00609                         async_client_data_destroy(client->data);
00610                 
00611                 free(client);
00612         }
00613         
00614         /*
00615          * Remove myself from the connection hash table.
00616          */
00617         futex_down(&async_futex);
00618         key = FIBRIL_connection->in_phone_hash;
00619         hash_table_remove(&conn_hash_table, &key, 1);
00620         futex_up(&async_futex);
00621         
00622         /*
00623          * Answer all remaining messages with EHANGUP.
00624          */
00625         while (!list_empty(&FIBRIL_connection->msg_queue)) {
00626                 msg_t *msg =
00627                     list_get_instance(FIBRIL_connection->msg_queue.next, msg_t,
00628                     link);
00629                 
00630                 list_remove(&msg->link);
00631                 ipc_answer_0(msg->callid, EHANGUP);
00632                 free(msg);
00633         }
00634         
00635         /*
00636          * If the connection was hung-up, answer the last call,
00637          * i.e. IPC_M_PHONE_HUNGUP.
00638          */
00639         if (FIBRIL_connection->close_callid)
00640                 ipc_answer_0(FIBRIL_connection->close_callid, EOK);
00641         
00642         free(FIBRIL_connection);
00643         return 0;
00644 }
00645 
00665 fid_t async_new_connection(sysarg_t in_task_hash, sysarg_t in_phone_hash,
00666     ipc_callid_t callid, ipc_call_t *call,
00667     void (*cfibril)(ipc_callid_t, ipc_call_t *))
00668 {
00669         connection_t *conn = malloc(sizeof(*conn));
00670         if (!conn) {
00671                 if (callid)
00672                         ipc_answer_0(callid, ENOMEM);
00673                 
00674                 return (uintptr_t) NULL;
00675         }
00676         
00677         conn->in_task_hash = in_task_hash;
00678         conn->in_phone_hash = in_phone_hash;
00679         list_initialize(&conn->msg_queue);
00680         conn->callid = callid;
00681         conn->close_callid = 0;
00682         
00683         if (call)
00684                 conn->call = *call;
00685         
00686         /* We will activate the fibril ASAP */
00687         conn->wdata.active = true;
00688         conn->cfibril = cfibril;
00689         conn->wdata.fid = fibril_create(connection_fibril, conn);
00690         
00691         if (conn->wdata.fid == 0) {
00692                 free(conn);
00693                 
00694                 if (callid)
00695                         ipc_answer_0(callid, ENOMEM);
00696                 
00697                 return (uintptr_t) NULL;
00698         }
00699         
00700         /* Add connection to the connection hash table */
00701         unsigned long key = conn->in_phone_hash;
00702         
00703         futex_down(&async_futex);
00704         hash_table_insert(&conn_hash_table, &key, &conn->link);
00705         futex_up(&async_futex);
00706         
00707         fibril_add_ready(conn->wdata.fid);
00708         
00709         return conn->wdata.fid;
00710 }
00711 
00721 static void handle_call(ipc_callid_t callid, ipc_call_t *call)
00722 {
00723         /* Unrouted call - take some default action */
00724         if ((callid & IPC_CALLID_NOTIFICATION)) {
00725                 process_notification(callid, call);
00726                 return;
00727         }
00728         
00729         switch (IPC_GET_IMETHOD(*call)) {
00730         case IPC_M_CONNECT_ME:
00731         case IPC_M_CONNECT_ME_TO:
00732                 /* Open new connection with fibril, etc. */
00733                 async_new_connection(call->in_task_hash, IPC_GET_ARG5(*call),
00734                     callid, call, client_connection);
00735                 return;
00736         }
00737         
00738         /* Try to route the call through the connection hash table */
00739         if (route_call(callid, call))
00740                 return;
00741         
00742         /* Unknown call from unknown phone - hang it up */
00743         ipc_answer_0(callid, EHANGUP);
00744 }
00745 
00747 static void handle_expired_timeouts(void)
00748 {
00749         struct timeval tv;
00750         gettimeofday(&tv, NULL);
00751         
00752         futex_down(&async_futex);
00753         
00754         link_t *cur = timeout_list.next;
00755         while (cur != &timeout_list) {
00756                 awaiter_t *waiter =
00757                     list_get_instance(cur, awaiter_t, to_event.link);
00758                 
00759                 if (tv_gt(&waiter->to_event.expires, &tv))
00760                         break;
00761                 
00762                 cur = cur->next;
00763                 
00764                 list_remove(&waiter->to_event.link);
00765                 waiter->to_event.inlist = false;
00766                 waiter->to_event.occurred = true;
00767                 
00768                 /*
00769                  * Redundant condition?
00770                  * The fibril should not be active when it gets here.
00771                  */
00772                 if (!waiter->active) {
00773                         waiter->active = true;
00774                         fibril_add_ready(waiter->fid);
00775                 }
00776         }
00777         
00778         futex_up(&async_futex);
00779 }
00780 
00786 static int async_manager_worker(void)
00787 {
00788         while (true) {
00789                 if (fibril_switch(FIBRIL_FROM_MANAGER)) {
00790                         futex_up(&async_futex);
00791                         /*
00792                          * async_futex is always held when entering a manager
00793                          * fibril.
00794                          */
00795                         continue;
00796                 }
00797                 
00798                 futex_down(&async_futex);
00799                 
00800                 suseconds_t timeout;
00801                 if (!list_empty(&timeout_list)) {
00802                         awaiter_t *waiter = list_get_instance(timeout_list.next,
00803                             awaiter_t, to_event.link);
00804                         
00805                         struct timeval tv;
00806                         gettimeofday(&tv, NULL);
00807                         
00808                         if (tv_gteq(&tv, &waiter->to_event.expires)) {
00809                                 futex_up(&async_futex);
00810                                 handle_expired_timeouts();
00811                                 continue;
00812                         } else
00813                                 timeout = tv_sub(&waiter->to_event.expires, &tv);
00814                 } else
00815                         timeout = SYNCH_NO_TIMEOUT;
00816                 
00817                 futex_up(&async_futex);
00818                 
00819                 atomic_inc(&threads_in_ipc_wait);
00820                 
00821                 ipc_call_t call;
00822                 ipc_callid_t callid = ipc_wait_cycle(&call, timeout,
00823                     SYNCH_FLAGS_NONE);
00824                 
00825                 atomic_dec(&threads_in_ipc_wait);
00826                 
00827                 if (!callid) {
00828                         handle_expired_timeouts();
00829                         continue;
00830                 }
00831                 
00832                 if (callid & IPC_CALLID_ANSWERED)
00833                         continue;
00834                 
00835                 handle_call(callid, &call);
00836         }
00837         
00838         return 0;
00839 }
00840 
00849 static int async_manager_fibril(void *arg)
00850 {
00851         futex_up(&async_futex);
00852         
00853         /*
00854          * async_futex is always locked when entering manager
00855          */
00856         async_manager_worker();
00857         
00858         return 0;
00859 }
00860 
00862 void async_create_manager(void)
00863 {
00864         fid_t fid = fibril_create(async_manager_fibril, NULL);
00865         if (fid != 0)
00866                 fibril_add_manager(fid);
00867 }
00868 
00870 void async_destroy_manager(void)
00871 {
00872         fibril_remove_manager();
00873 }
00874 
00878 void __async_init(void)
00879 {
00880         if (!hash_table_create(&client_hash_table, CLIENT_HASH_TABLE_BUCKETS, 1,
00881             &client_hash_table_ops))
00882                 abort();
00883         
00884         if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_BUCKETS, 1,
00885             &conn_hash_table_ops))
00886                 abort();
00887 }
00888 
00901 static void reply_received(void *arg, int retval, ipc_call_t *data)
00902 {
00903         futex_down(&async_futex);
00904         
00905         amsg_t *msg = (amsg_t *) arg;
00906         msg->retval = retval;
00907         
00908         /* Copy data after futex_down, just in case the call was detached */
00909         if ((msg->dataptr) && (data))
00910                 *msg->dataptr = *data;
00911         
00912         write_barrier();
00913         
00914         /* Remove message from timeout list */
00915         if (msg->wdata.to_event.inlist)
00916                 list_remove(&msg->wdata.to_event.link);
00917         
00918         msg->done = true;
00919         if (!msg->wdata.active) {
00920                 msg->wdata.active = true;
00921                 fibril_add_ready(msg->wdata.fid);
00922         }
00923         
00924         futex_up(&async_futex);
00925 }
00926 
00944 aid_t async_send_fast(int phoneid, sysarg_t method, sysarg_t arg1,
00945     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
00946 {
00947         amsg_t *msg = malloc(sizeof(amsg_t));
00948         
00949         if (!msg)
00950                 return 0;
00951         
00952         msg->done = false;
00953         msg->dataptr = dataptr;
00954         
00955         msg->wdata.to_event.inlist = false;
00956         
00957         /*
00958          * We may sleep in the next method,
00959          * but it will use its own means
00960          */
00961         msg->wdata.active = true;
00962         
00963         ipc_call_async_4(phoneid, method, arg1, arg2, arg3, arg4, msg,
00964             reply_received, true);
00965         
00966         return (aid_t) msg;
00967 }
00968 
00987 aid_t async_send_slow(int phoneid, sysarg_t method, sysarg_t arg1,
00988     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
00989     ipc_call_t *dataptr)
00990 {
00991         amsg_t *msg = malloc(sizeof(amsg_t));
00992         
00993         if (!msg)
00994                 return 0;
00995         
00996         msg->done = false;
00997         msg->dataptr = dataptr;
00998         
00999         msg->wdata.to_event.inlist = false;
01000         
01001         /*
01002          * We may sleep in the next method,
01003          * but it will use its own means
01004          */
01005         msg->wdata.active = true;
01006         
01007         ipc_call_async_5(phoneid, method, arg1, arg2, arg3, arg4, arg5, msg,
01008             reply_received, true);
01009         
01010         return (aid_t) msg;
01011 }
01012 
01020 void async_wait_for(aid_t amsgid, sysarg_t *retval)
01021 {
01022         amsg_t *msg = (amsg_t *) amsgid;
01023         
01024         futex_down(&async_futex);
01025         if (msg->done) {
01026                 futex_up(&async_futex);
01027                 goto done;
01028         }
01029         
01030         msg->wdata.fid = fibril_get_id();
01031         msg->wdata.active = false;
01032         msg->wdata.to_event.inlist = false;
01033         
01034         /* Leave the async_futex locked when entering this function */
01035         fibril_switch(FIBRIL_TO_MANAGER);
01036         
01037         /* Futex is up automatically after fibril_switch */
01038         
01039 done:
01040         if (retval)
01041                 *retval = msg->retval;
01042         
01043         free(msg);
01044 }
01045 
01056 int async_wait_timeout(aid_t amsgid, sysarg_t *retval, suseconds_t timeout)
01057 {
01058         amsg_t *msg = (amsg_t *) amsgid;
01059         
01060         /* TODO: Let it go through the event read at least once */
01061         if (timeout < 0)
01062                 return ETIMEOUT;
01063         
01064         futex_down(&async_futex);
01065         if (msg->done) {
01066                 futex_up(&async_futex);
01067                 goto done;
01068         }
01069         
01070         gettimeofday(&msg->wdata.to_event.expires, NULL);
01071         tv_add(&msg->wdata.to_event.expires, timeout);
01072         
01073         msg->wdata.fid = fibril_get_id();
01074         msg->wdata.active = false;
01075         async_insert_timeout(&msg->wdata);
01076         
01077         /* Leave the async_futex locked when entering this function */
01078         fibril_switch(FIBRIL_TO_MANAGER);
01079         
01080         /* Futex is up automatically after fibril_switch */
01081         
01082         if (!msg->done)
01083                 return ETIMEOUT;
01084         
01085 done:
01086         if (retval)
01087                 *retval = msg->retval;
01088         
01089         free(msg);
01090         
01091         return 0;
01092 }
01093 
01101 void async_usleep(suseconds_t timeout)
01102 {
01103         amsg_t *msg = malloc(sizeof(amsg_t));
01104         
01105         if (!msg)
01106                 return;
01107         
01108         msg->wdata.fid = fibril_get_id();
01109         msg->wdata.active = false;
01110         
01111         gettimeofday(&msg->wdata.to_event.expires, NULL);
01112         tv_add(&msg->wdata.to_event.expires, timeout);
01113         
01114         futex_down(&async_futex);
01115         
01116         async_insert_timeout(&msg->wdata);
01117         
01118         /* Leave the async_futex locked when entering this function */
01119         fibril_switch(FIBRIL_TO_MANAGER);
01120         
01121         /* Futex is up automatically after fibril_switch() */
01122         
01123         free(msg);
01124 }
01125 
01131 void async_set_client_connection(async_client_conn_t conn)
01132 {
01133         client_connection = conn;
01134 }
01135 
01141 void async_set_interrupt_received(async_client_conn_t intr)
01142 {
01143         interrupt_received = intr;
01144 }
01145 
01168 sysarg_t async_req_fast(int phoneid, sysarg_t method, sysarg_t arg1,
01169     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
01170     sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
01171 {
01172         ipc_call_t result;
01173         aid_t eid = async_send_4(phoneid, method, arg1, arg2, arg3, arg4,
01174             &result);
01175         
01176         sysarg_t rc;
01177         async_wait_for(eid, &rc);
01178         
01179         if (r1)
01180                 *r1 = IPC_GET_ARG1(result);
01181         
01182         if (r2)
01183                 *r2 = IPC_GET_ARG2(result);
01184         
01185         if (r3)
01186                 *r3 = IPC_GET_ARG3(result);
01187         
01188         if (r4)
01189                 *r4 = IPC_GET_ARG4(result);
01190         
01191         if (r5)
01192                 *r5 = IPC_GET_ARG5(result);
01193         
01194         return rc;
01195 }
01196 
01217 sysarg_t async_req_slow(int phoneid, sysarg_t method, sysarg_t arg1,
01218     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
01219     sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
01220 {
01221         ipc_call_t result;
01222         aid_t eid = async_send_5(phoneid, method, arg1, arg2, arg3, arg4, arg5,
01223             &result);
01224         
01225         sysarg_t rc;
01226         async_wait_for(eid, &rc);
01227         
01228         if (r1)
01229                 *r1 = IPC_GET_ARG1(result);
01230         
01231         if (r2)
01232                 *r2 = IPC_GET_ARG2(result);
01233         
01234         if (r3)
01235                 *r3 = IPC_GET_ARG3(result);
01236         
01237         if (r4)
01238                 *r4 = IPC_GET_ARG4(result);
01239         
01240         if (r5)
01241                 *r5 = IPC_GET_ARG5(result);
01242         
01243         return rc;
01244 }
01245 
01246 void async_msg_0(int phone, sysarg_t imethod)
01247 {
01248         ipc_call_async_0(phone, imethod, NULL, NULL, true);
01249 }
01250 
01251 void async_msg_1(int phone, sysarg_t imethod, sysarg_t arg1)
01252 {
01253         ipc_call_async_1(phone, imethod, arg1, NULL, NULL, true);
01254 }
01255 
01256 void async_msg_2(int phone, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2)
01257 {
01258         ipc_call_async_2(phone, imethod, arg1, arg2, NULL, NULL, true);
01259 }
01260 
01261 void async_msg_3(int phone, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2,
01262     sysarg_t arg3)
01263 {
01264         ipc_call_async_3(phone, imethod, arg1, arg2, arg3, NULL, NULL, true);
01265 }
01266 
01267 void async_msg_4(int phone, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2,
01268     sysarg_t arg3, sysarg_t arg4)
01269 {
01270         ipc_call_async_4(phone, imethod, arg1, arg2, arg3, arg4, NULL, NULL,
01271             true);
01272 }
01273 
01274 void async_msg_5(int phone, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2,
01275     sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
01276 {
01277         ipc_call_async_5(phone, imethod, arg1, arg2, arg3, arg4, arg5, NULL,
01278             NULL, true);
01279 }
01280 
01281 sysarg_t async_answer_0(ipc_callid_t callid, sysarg_t retval)
01282 {
01283         return ipc_answer_0(callid, retval);
01284 }
01285 
01286 sysarg_t async_answer_1(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1)
01287 {
01288         return ipc_answer_1(callid, retval, arg1);
01289 }
01290 
01291 sysarg_t async_answer_2(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
01292     sysarg_t arg2)
01293 {
01294         return ipc_answer_2(callid, retval, arg1, arg2);
01295 }
01296 
01297 sysarg_t async_answer_3(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
01298     sysarg_t arg2, sysarg_t arg3)
01299 {
01300         return ipc_answer_3(callid, retval, arg1, arg2, arg3);
01301 }
01302 
01303 sysarg_t async_answer_4(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
01304     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
01305 {
01306         return ipc_answer_4(callid, retval, arg1, arg2, arg3, arg4);
01307 }
01308 
01309 sysarg_t async_answer_5(ipc_callid_t callid, sysarg_t retval, sysarg_t arg1,
01310     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
01311 {
01312         return ipc_answer_5(callid, retval, arg1, arg2, arg3, arg4, arg5);
01313 }
01314 
01315 int async_forward_fast(ipc_callid_t callid, int phoneid, sysarg_t imethod,
01316     sysarg_t arg1, sysarg_t arg2, unsigned int mode)
01317 {
01318         return ipc_forward_fast(callid, phoneid, imethod, arg1, arg2, mode);
01319 }
01320 
01321 int async_forward_slow(ipc_callid_t callid, int phoneid, sysarg_t imethod,
01322     sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
01323     unsigned int mode)
01324 {
01325         return ipc_forward_slow(callid, phoneid, imethod, arg1, arg2, arg3, arg4,
01326             arg5, mode);
01327 }
01328 
01342 int async_connect_to_me(int phone, sysarg_t arg1, sysarg_t arg2,
01343     sysarg_t arg3, async_client_conn_t client_receiver)
01344 {
01345         sysarg_t task_hash;
01346         sysarg_t phone_hash;
01347         int rc = async_req_3_5(phone, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
01348             NULL, NULL, NULL, &task_hash, &phone_hash);
01349         if (rc != EOK)
01350                 return rc;
01351         
01352         if (client_receiver != NULL)
01353                 async_new_connection(task_hash, phone_hash, 0, NULL,
01354                     client_receiver);
01355         
01356         return EOK;
01357 }
01358 
01371 int async_connect_me_to(int phone, sysarg_t arg1, sysarg_t arg2,
01372     sysarg_t arg3)
01373 {
01374         sysarg_t newphid;
01375         int rc = async_req_3_5(phone, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3,
01376             NULL, NULL, NULL, NULL, &newphid);
01377         
01378         if (rc != EOK)
01379                 return rc;
01380         
01381         return newphid;
01382 }
01383 
01397 int async_connect_me_to_blocking(int phoneid, sysarg_t arg1, sysarg_t arg2,
01398     sysarg_t arg3)
01399 {
01400         sysarg_t newphid;
01401         int rc = async_req_4_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3,
01402             IPC_FLAG_BLOCKING, NULL, NULL, NULL, NULL, &newphid);
01403         
01404         if (rc != EOK)
01405                 return rc;
01406         
01407         return newphid;
01408 }
01409 
01413 int async_connect_kbox(task_id_t id)
01414 {
01415         return ipc_connect_kbox(id);
01416 }
01417 
01425 int async_hangup(int phone)
01426 {
01427         return ipc_hangup(phone);
01428 }
01429 
01431 void async_poke(void)
01432 {
01433         ipc_poke();
01434 }
01435 
01447 int async_share_in_start(int phoneid, void *dst, size_t size, sysarg_t arg,
01448     unsigned int *flags)
01449 {
01450         sysarg_t tmp_flags;
01451         int res = async_req_3_2(phoneid, IPC_M_SHARE_IN, (sysarg_t) dst,
01452             (sysarg_t) size, arg, NULL, &tmp_flags);
01453         
01454         if (flags)
01455                 *flags = (unsigned int) tmp_flags;
01456         
01457         return res;
01458 }
01459 
01474 bool async_share_in_receive(ipc_callid_t *callid, size_t *size)
01475 {
01476         assert(callid);
01477         assert(size);
01478         
01479         ipc_call_t data;
01480         *callid = async_get_call(&data);
01481         
01482         if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
01483                 return false;
01484         
01485         *size = (size_t) IPC_GET_ARG2(data);
01486         return true;
01487 }
01488 
01502 int async_share_in_finalize(ipc_callid_t callid, void *src, unsigned int flags)
01503 {
01504         return ipc_share_in_finalize(callid, src, flags);
01505 }
01506 
01516 int async_share_out_start(int phoneid, void *src, unsigned int flags)
01517 {
01518         return async_req_3_0(phoneid, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
01519             (sysarg_t) flags);
01520 }
01521 
01537 bool async_share_out_receive(ipc_callid_t *callid, size_t *size, unsigned int *flags)
01538 {
01539         assert(callid);
01540         assert(size);
01541         assert(flags);
01542         
01543         ipc_call_t data;
01544         *callid = async_get_call(&data);
01545         
01546         if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
01547                 return false;
01548         
01549         *size = (size_t) IPC_GET_ARG2(data);
01550         *flags = (unsigned int) IPC_GET_ARG3(data);
01551         return true;
01552 }
01553 
01566 int async_share_out_finalize(ipc_callid_t callid, void *dst)
01567 {
01568         return ipc_share_out_finalize(callid, dst);
01569 }
01570 
01579 aid_t async_data_read(int phoneid, void *dst, size_t size, ipc_call_t *dataptr)
01580 {
01581         return async_send_2(phoneid, IPC_M_DATA_READ, (sysarg_t) dst,
01582             (sysarg_t) size, dataptr);
01583 }
01584 
01595 int
01596 async_data_read_start_generic(int phoneid, void *dst, size_t size, int flags)
01597 {
01598         return async_req_3_0(phoneid, IPC_M_DATA_READ, (sysarg_t) dst,
01599             (sysarg_t) size, (sysarg_t) flags);
01600 }
01601 
01616 bool async_data_read_receive(ipc_callid_t *callid, size_t *size)
01617 {
01618         assert(callid);
01619         
01620         ipc_call_t data;
01621         *callid = async_get_call(&data);
01622         
01623         if (IPC_GET_IMETHOD(data) != IPC_M_DATA_READ)
01624                 return false;
01625         
01626         if (size)
01627                 *size = (size_t) IPC_GET_ARG2(data);
01628         
01629         return true;
01630 }
01631 
01646 int async_data_read_finalize(ipc_callid_t callid, const void *src, size_t size)
01647 {
01648         return ipc_data_read_finalize(callid, src, size);
01649 }
01650 
01654 int async_data_read_forward_fast(int phoneid, sysarg_t method, sysarg_t arg1,
01655     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
01656 {
01657         ipc_callid_t callid;
01658         if (!async_data_read_receive(&callid, NULL)) {
01659                 ipc_answer_0(callid, EINVAL);
01660                 return EINVAL;
01661         }
01662         
01663         aid_t msg = async_send_fast(phoneid, method, arg1, arg2, arg3, arg4,
01664             dataptr);
01665         if (msg == 0) {
01666                 ipc_answer_0(callid, EINVAL);
01667                 return EINVAL;
01668         }
01669         
01670         int retval = ipc_forward_fast(callid, phoneid, 0, 0, 0,
01671             IPC_FF_ROUTE_FROM_ME);
01672         if (retval != EOK) {
01673                 async_wait_for(msg, NULL);
01674                 ipc_answer_0(callid, retval);
01675                 return retval;
01676         }
01677         
01678         sysarg_t rc;
01679         async_wait_for(msg, &rc);
01680         
01681         return (int) rc;
01682 }
01683 
01694 int
01695 async_data_write_start_generic(int phoneid, const void *src, size_t size,
01696     int flags)
01697 {
01698         return async_req_3_0(phoneid, IPC_M_DATA_WRITE, (sysarg_t) src,
01699             (sysarg_t) size, (sysarg_t) flags);
01700 }
01701 
01716 bool async_data_write_receive(ipc_callid_t *callid, size_t *size)
01717 {
01718         assert(callid);
01719         
01720         ipc_call_t data;
01721         *callid = async_get_call(&data);
01722         
01723         if (IPC_GET_IMETHOD(data) != IPC_M_DATA_WRITE)
01724                 return false;
01725         
01726         if (size)
01727                 *size = (size_t) IPC_GET_ARG2(data);
01728         
01729         return true;
01730 }
01731 
01745 int async_data_write_finalize(ipc_callid_t callid, void *dst, size_t size)
01746 {
01747         return ipc_data_write_finalize(callid, dst, size);
01748 }
01749 
01771 int async_data_write_accept(void **data, const bool nullterm,
01772     const size_t min_size, const size_t max_size, const size_t granularity,
01773     size_t *received)
01774 {
01775         ipc_callid_t callid;
01776         size_t size;
01777         if (!async_data_write_receive(&callid, &size)) {
01778                 ipc_answer_0(callid, EINVAL);
01779                 return EINVAL;
01780         }
01781         
01782         if (size < min_size) {
01783                 ipc_answer_0(callid, EINVAL);
01784                 return EINVAL;
01785         }
01786         
01787         if ((max_size > 0) && (size > max_size)) {
01788                 ipc_answer_0(callid, EINVAL);
01789                 return EINVAL;
01790         }
01791         
01792         if ((granularity > 0) && ((size % granularity) != 0)) {
01793                 ipc_answer_0(callid, EINVAL);
01794                 return EINVAL;
01795         }
01796         
01797         void *_data;
01798         
01799         if (nullterm)
01800                 _data = malloc(size + 1);
01801         else
01802                 _data = malloc(size);
01803         
01804         if (_data == NULL) {
01805                 ipc_answer_0(callid, ENOMEM);
01806                 return ENOMEM;
01807         }
01808         
01809         int rc = async_data_write_finalize(callid, _data, size);
01810         if (rc != EOK) {
01811                 free(_data);
01812                 return rc;
01813         }
01814         
01815         if (nullterm)
01816                 ((char *) _data)[size] = 0;
01817         
01818         *data = _data;
01819         if (received != NULL)
01820                 *received = size;
01821         
01822         return EOK;
01823 }
01824 
01832 void async_data_write_void(sysarg_t retval)
01833 {
01834         ipc_callid_t callid;
01835         async_data_write_receive(&callid, NULL);
01836         ipc_answer_0(callid, retval);
01837 }
01838 
01842 int async_data_write_forward_fast(int phoneid, sysarg_t method, sysarg_t arg1,
01843     sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
01844 {
01845         ipc_callid_t callid;
01846         if (!async_data_write_receive(&callid, NULL)) {
01847                 ipc_answer_0(callid, EINVAL);
01848                 return EINVAL;
01849         }
01850         
01851         aid_t msg = async_send_fast(phoneid, method, arg1, arg2, arg3, arg4,
01852             dataptr);
01853         if (msg == 0) {
01854                 ipc_answer_0(callid, EINVAL);
01855                 return EINVAL;
01856         }
01857         
01858         int retval = ipc_forward_fast(callid, phoneid, 0, 0, 0,
01859             IPC_FF_ROUTE_FROM_ME);
01860         if (retval != EOK) {
01861                 async_wait_for(msg, NULL);
01862                 ipc_answer_0(callid, retval);
01863                 return retval;
01864         }
01865         
01866         sysarg_t rc;
01867         async_wait_for(msg, &rc);
01868         
01869         return (int) rc;
01870 }
01871 

Generated on Thu Jun 2 07:45:46 2011 for HelenOS/USB by  doxygen 1.4.7