Ignore:
File:
1 edited

Legend:

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

    r706b4de r503ffce  
    11/*
    22 * Copyright (c) 2006 Ondrej Palkovsky
     3 * Copyright (c) 2017 Jakub Jermar
    34 * All rights reserved.
    45 *
     
    5051
    5152/**
    52  * Structures of this type are used for keeping track
    53  * of sent asynchronous calls and queing unsent calls.
    54  */
    55 typedef struct {
    56         link_t list;
    57        
     53 * Structures of this type are used for keeping track of sent asynchronous calls.
     54 */
     55typedef struct async_call {
    5856        ipc_async_callback_t callback;
    5957        void *private;
    6058       
    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;
     59        struct {
     60                ipc_call_t data;
     61                int phoneid;
     62        } msg;
    7163} async_call_t;
    72 
    73 LIST_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  */
    81 LIST_INITIALIZE(queued_calls);
    82 
    83 static 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  */
    93 static 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 }
    9764
    9865/** Prologue for ipc_call_async_*() functions.
     
    133100        if (!call) {
    134101                /* Nothing to do regardless if failed or not */
    135                 futex_unlock(&ipc_futex);
    136102                return;
    137103        }
    138104       
    139105        if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
    140                 futex_unlock(&ipc_futex);
    141                
    142106                /* Call asynchronous handler with error code */
    143107                if (call->callback)
     
    147111                return;
    148112        }
    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);
    155113}
    156114
    157115/** Fast asynchronous call.
    158116 *
    159  * This function can only handle four arguments of payload. It is, however,
     117 * This function can only handle three arguments of payload. It is, however,
    160118 * faster than the more generic ipc_call_async_slow().
    161119 *
     
    171129 * @param arg2        Service-defined payload argument.
    172130 * @param arg3        Service-defined payload argument.
    173  * @param arg4        Service-defined payload argument.
    174131 * @param private     Argument to be passed to the answer/error callback.
    175132 * @param callback    Answer or error callback.
    176133 */
    177134void ipc_call_async_fast(int phoneid, sysarg_t imethod, sysarg_t arg1,
    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);
     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       
    195141        ipc_callid_t callid = __SYSCALL6(SYS_IPC_CALL_ASYNC_FAST, phoneid,
    196             imethod, arg1, arg2, arg3, arg4);
     142            imethod, arg1, arg2, arg3, (sysarg_t) call);
    197143       
    198144        ipc_finish_async(callid, phoneid, call);
     
    225171                return;
    226172       
    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);
     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);
    242182       
    243183        ipc_finish_async(callid, phoneid, call);
     
    296236}
    297237
    298 /** Try to dispatch queued calls from the async queue.
    299  *
    300  */
    301 static 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 
    342238/** 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.
    350239 *
    351240 * @param callid Hash of the received answer.
    352241 * @param data   Call data of the answer.
    353  *
    354242 */
    355243static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
    356244{
    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);
     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);
    382253}
    383254
     
    388259 * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking).
    389260 *
    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  *
     261 * @return Hash of the call.
    394262 */
    395263ipc_callid_t ipc_wait_cycle(ipc_call_t *call, sysarg_t usec,
     
    400268       
    401269        /* Handle received answers */
    402         if (callid & IPC_CALLID_ANSWERED) {
     270        if (callid && (call->flags & IPC_CALLID_ANSWERED))
    403271                handle_answer(callid, call);
    404                 dispatch_queued_calls();
    405         }
    406272       
    407273        return callid;
     
    432298        do {
    433299                callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
    434         } while (callid & IPC_CALLID_ANSWERED);
     300        } while (callid && (call->flags & IPC_CALLID_ANSWERED));
    435301       
    436302        return callid;
     
    453319                callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
    454320                    SYNCH_FLAGS_NON_BLOCKING);
    455         } while (callid & IPC_CALLID_ANSWERED);
     321        } while (callid && (call->flags & IPC_CALLID_ANSWERED));
    456322       
    457323        return callid;
Note: See TracChangeset for help on using the changeset viewer.