Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/generic/ipc.c

    r503ffce r706b4de  
    11/*
    22 * Copyright (c) 2006 Ondrej Palkovsky
    3  * Copyright (c) 2017 Jakub Jermar
    43 * All rights reserved.
    54 *
     
    5150
    5251/**
    53  * Structures of this type are used for keeping track of sent asynchronous calls.
    54  */
    55 typedef struct async_call {
     52 * Structures of this type are used for keeping track
     53 * of sent asynchronous calls and queing unsent calls.
     54 */
     55typedef struct {
     56        link_t list;
     57       
    5658        ipc_async_callback_t callback;
    5759        void *private;
    5860       
    59         struct {
    60                 ipc_call_t data;
    61                 int phoneid;
    62         } msg;
     61        union {
     62                ipc_callid_t callid;
     63                struct {
     64                        ipc_call_t data;
     65                        int phoneid;
     66                } msg;
     67        } u;
     68       
     69        /** Fibril waiting for sending this call. */
     70        fid_t fid;
    6371} async_call_t;
     72
     73LIST_INITIALIZE(dispatched_calls);
     74
     75/** List of asynchronous calls that were not accepted by kernel.
     76 *
     77 * Protected by async_futex, because if the call is not accepted
     78 * by the kernel, the async framework is used automatically.
     79 *
     80 */
     81LIST_INITIALIZE(queued_calls);
     82
     83static futex_t ipc_futex = FUTEX_INITIALIZER;
     84
     85/** Send asynchronous message via syscall.
     86 *
     87 * @param phoneid Phone handle for the call.
     88 * @param data    Call data with the request.
     89 *
     90 * @return Hash of the call or an error code.
     91 *
     92 */
     93static ipc_callid_t ipc_call_async_internal(int phoneid, ipc_call_t *data)
     94{
     95        return __SYSCALL2(SYS_IPC_CALL_ASYNC_SLOW, phoneid, (sysarg_t) data);
     96}
    6497
    6598/** Prologue for ipc_call_async_*() functions.
     
    100133        if (!call) {
    101134                /* Nothing to do regardless if failed or not */
     135                futex_unlock(&ipc_futex);
    102136                return;
    103137        }
    104138       
    105139        if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
     140                futex_unlock(&ipc_futex);
     141               
    106142                /* Call asynchronous handler with error code */
    107143                if (call->callback)
     
    111147                return;
    112148        }
     149       
     150        call->u.callid = callid;
     151       
     152        /* Add call to the list of dispatched calls */
     153        list_append(&call->list, &dispatched_calls);
     154        futex_unlock(&ipc_futex);
    113155}
    114156
    115157/** Fast asynchronous call.
    116158 *
    117  * This function can only handle three arguments of payload. It is, however,
     159 * This function can only handle four arguments of payload. It is, however,
    118160 * faster than the more generic ipc_call_async_slow().
    119161 *
     
    129171 * @param arg2        Service-defined payload argument.
    130172 * @param arg3        Service-defined payload argument.
     173 * @param arg4        Service-defined payload argument.
    131174 * @param private     Argument to be passed to the answer/error callback.
    132175 * @param callback    Answer or error callback.
    133176 */
    134177void ipc_call_async_fast(int phoneid, sysarg_t imethod, sysarg_t arg1,
    135     sysarg_t arg2, sysarg_t arg3, void *private, ipc_async_callback_t callback)
    136 {
    137         async_call_t *call = ipc_prepare_async(private, callback);
    138         if (!call)
    139                 return;
    140        
     178    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, void *private,
     179    ipc_async_callback_t callback)
     180{
     181        async_call_t *call = NULL;
     182       
     183        if (callback) {
     184                call = ipc_prepare_async(private, callback);
     185                if (!call)
     186                        return;
     187        }
     188       
     189        /*
     190         * We need to make sure that we get callid
     191         * before another thread accesses the queue again.
     192         */
     193       
     194        futex_lock(&ipc_futex);
    141195        ipc_callid_t callid = __SYSCALL6(SYS_IPC_CALL_ASYNC_FAST, phoneid,
    142             imethod, arg1, arg2, arg3, (sysarg_t) call);
     196            imethod, arg1, arg2, arg3, arg4);
    143197       
    144198        ipc_finish_async(callid, phoneid, call);
     
    171225                return;
    172226       
    173         IPC_SET_IMETHOD(call->msg.data, imethod);
    174         IPC_SET_ARG1(call->msg.data, arg1);
    175         IPC_SET_ARG2(call->msg.data, arg2);
    176         IPC_SET_ARG3(call->msg.data, arg3);
    177         IPC_SET_ARG4(call->msg.data, arg4);
    178         IPC_SET_ARG5(call->msg.data, arg5);
    179        
    180         ipc_callid_t callid = __SYSCALL3(SYS_IPC_CALL_ASYNC_SLOW, phoneid,
    181             (sysarg_t) &call->msg.data, (sysarg_t) call);
     227        IPC_SET_IMETHOD(call->u.msg.data, imethod);
     228        IPC_SET_ARG1(call->u.msg.data, arg1);
     229        IPC_SET_ARG2(call->u.msg.data, arg2);
     230        IPC_SET_ARG3(call->u.msg.data, arg3);
     231        IPC_SET_ARG4(call->u.msg.data, arg4);
     232        IPC_SET_ARG5(call->u.msg.data, arg5);
     233       
     234        /*
     235         * We need to make sure that we get callid
     236         * before another threadaccesses the queue again.
     237         */
     238       
     239        futex_lock(&ipc_futex);
     240        ipc_callid_t callid =
     241            ipc_call_async_internal(phoneid, &call->u.msg.data);
    182242       
    183243        ipc_finish_async(callid, phoneid, call);
     
    236296}
    237297
     298/** Try to dispatch queued calls from the async queue.
     299 *
     300 */
     301static void dispatch_queued_calls(void)
     302{
     303        /** @todo
     304         * Integrate intelligently ipc_futex so that it is locked during
     305         * ipc_call_async_*() until it is added to dispatched_calls.
     306         */
     307       
     308        futex_down(&async_futex);
     309       
     310        while (!list_empty(&queued_calls)) {
     311                async_call_t *call =
     312                    list_get_instance(list_first(&queued_calls), async_call_t, list);
     313                ipc_callid_t callid =
     314                    ipc_call_async_internal(call->u.msg.phoneid, &call->u.msg.data);
     315               
     316                list_remove(&call->list);
     317               
     318                futex_up(&async_futex);
     319               
     320                assert(call->fid);
     321                fibril_add_ready(call->fid);
     322               
     323                if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
     324                        if (call->callback)
     325                                call->callback(call->private, ENOENT, NULL);
     326                       
     327                        free(call);
     328                } else {
     329                        call->u.callid = callid;
     330                       
     331                        futex_lock(&ipc_futex);
     332                        list_append(&call->list, &dispatched_calls);
     333                        futex_unlock(&ipc_futex);
     334                }
     335               
     336                futex_down(&async_futex);
     337        }
     338       
     339        futex_up(&async_futex);
     340}
     341
    238342/** Handle received answer.
     343 *
     344 * Find the hash of the answer and call the answer callback.
     345 *
     346 * The answer has the same hash as the request OR'ed with
     347 * the IPC_CALLID_ANSWERED bit.
     348 *
     349 * @todo Use hash table.
    239350 *
    240351 * @param callid Hash of the received answer.
    241352 * @param data   Call data of the answer.
     353 *
    242354 */
    243355static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
    244356{
    245         async_call_t *call = data->label;
    246 
    247         if (!call)
    248                 return;
    249 
    250         if (call->callback)
    251                 call->callback(call->private, IPC_GET_RETVAL(*data), data);
    252         free(call);
     357        callid &= ~IPC_CALLID_ANSWERED;
     358       
     359        futex_lock(&ipc_futex);
     360       
     361        link_t *item;
     362        for (item = dispatched_calls.head.next; item != &dispatched_calls.head;
     363            item = item->next) {
     364                async_call_t *call =
     365                    list_get_instance(item, async_call_t, list);
     366               
     367                if (call->u.callid == callid) {
     368                        list_remove(&call->list);
     369                       
     370                        futex_unlock(&ipc_futex);
     371                       
     372                        if (call->callback)
     373                                call->callback(call->private,
     374                                    IPC_GET_RETVAL(*data), data);
     375                       
     376                        free(call);
     377                        return;
     378                }
     379        }
     380       
     381        futex_unlock(&ipc_futex);
    253382}
    254383
     
    259388 * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking).
    260389 *
    261  * @return Hash of the call.
     390 * @return Hash of the call. Note that certain bits have special
     391 *         meaning: IPC_CALLID_ANSWERED is set in an answer
     392 *         and IPC_CALLID_NOTIFICATION is used for notifications.
     393 *
    262394 */
    263395ipc_callid_t ipc_wait_cycle(ipc_call_t *call, sysarg_t usec,
     
    268400       
    269401        /* Handle received answers */
    270         if (callid && (call->flags & IPC_CALLID_ANSWERED))
     402        if (callid & IPC_CALLID_ANSWERED) {
    271403                handle_answer(callid, call);
     404                dispatch_queued_calls();
     405        }
    272406       
    273407        return callid;
     
    298432        do {
    299433                callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
    300         } while (callid && (call->flags & IPC_CALLID_ANSWERED));
     434        } while (callid & IPC_CALLID_ANSWERED);
    301435       
    302436        return callid;
     
    319453                callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
    320454                    SYNCH_FLAGS_NON_BLOCKING);
    321         } while (callid && (call->flags & IPC_CALLID_ANSWERED));
     455        } while (callid & IPC_CALLID_ANSWERED);
    322456       
    323457        return callid;
Note: See TracChangeset for help on using the changeset viewer.