Changes in uspace/lib/c/generic/ipc.c [503ffce:706b4de] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/c/generic/ipc.c
r503ffce r706b4de 1 1 /* 2 2 * Copyright (c) 2006 Ondrej Palkovsky 3 * Copyright (c) 2017 Jakub Jermar4 3 * All rights reserved. 5 4 * … … 51 50 52 51 /** 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 */ 55 typedef struct { 56 link_t list; 57 56 58 ipc_async_callback_t callback; 57 59 void *private; 58 60 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; 63 71 } 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 } 64 97 65 98 /** Prologue for ipc_call_async_*() functions. … … 100 133 if (!call) { 101 134 /* Nothing to do regardless if failed or not */ 135 futex_unlock(&ipc_futex); 102 136 return; 103 137 } 104 138 105 139 if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) { 140 futex_unlock(&ipc_futex); 141 106 142 /* Call asynchronous handler with error code */ 107 143 if (call->callback) … … 111 147 return; 112 148 } 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); 113 155 } 114 156 115 157 /** Fast asynchronous call. 116 158 * 117 * This function can only handle threearguments of payload. It is, however,159 * This function can only handle four arguments of payload. It is, however, 118 160 * faster than the more generic ipc_call_async_slow(). 119 161 * … … 129 171 * @param arg2 Service-defined payload argument. 130 172 * @param arg3 Service-defined payload argument. 173 * @param arg4 Service-defined payload argument. 131 174 * @param private Argument to be passed to the answer/error callback. 132 175 * @param callback Answer or error callback. 133 176 */ 134 177 void 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); 141 195 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); 143 197 144 198 ipc_finish_async(callid, phoneid, call); … … 171 225 return; 172 226 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); 182 242 183 243 ipc_finish_async(callid, phoneid, call); … … 236 296 } 237 297 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 238 342 /** 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. 239 350 * 240 351 * @param callid Hash of the received answer. 241 352 * @param data Call data of the answer. 353 * 242 354 */ 243 355 static void handle_answer(ipc_callid_t callid, ipc_call_t *data) 244 356 { 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); 253 382 } 254 383 … … 259 388 * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking). 260 389 * 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 * 262 394 */ 263 395 ipc_callid_t ipc_wait_cycle(ipc_call_t *call, sysarg_t usec, … … 268 400 269 401 /* Handle received answers */ 270 if (callid & & (call->flags & IPC_CALLID_ANSWERED))402 if (callid & IPC_CALLID_ANSWERED) { 271 403 handle_answer(callid, call); 404 dispatch_queued_calls(); 405 } 272 406 273 407 return callid; … … 298 432 do { 299 433 callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE); 300 } while (callid & & (call->flags & IPC_CALLID_ANSWERED));434 } while (callid & IPC_CALLID_ANSWERED); 301 435 302 436 return callid; … … 319 453 callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT, 320 454 SYNCH_FLAGS_NON_BLOCKING); 321 } while (callid & & (call->flags & IPC_CALLID_ANSWERED));455 } while (callid & IPC_CALLID_ANSWERED); 322 456 323 457 return callid;
Note:
See TracChangeset
for help on using the changeset viewer.