source: mainline/uspace/lib/c/generic/async/client.c@ 95faa4d

Last change on this file since 95faa4d was 95faa4d, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Convert a bit of devman.

  • Property mode set to 100644
File size: 38.6 KB
Line 
1/*
2 * Copyright (c) 2006 Ondrej Palkovsky
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.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/** @file
33 */
34
35/**
36 * Asynchronous library
37 *
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.
41 *
42 * You should be able to write very simple multithreaded programs. The async
43 * framework will automatically take care of most of the synchronization
44 * problems.
45 *
46 * Example of use (pseudo C):
47 *
48 * 1) Multithreaded client application
49 *
50 * fibril_create(fibril1, ...);
51 * fibril_create(fibril2, ...);
52 * ...
53 *
54 * int fibril1(void *arg)
55 * {
56 * conn = async_connect_me_to(...);
57 *
58 * exch = async_exchange_begin(conn);
59 * c1 = async_send(exch);
60 * async_exchange_end(exch);
61 *
62 * exch = async_exchange_begin(conn);
63 * c2 = async_send(exch);
64 * async_exchange_end(exch);
65 *
66 * async_wait_for(c1);
67 * async_wait_for(c2);
68 * ...
69 * }
70 *
71 *
72 * 2) Multithreaded server application
73 *
74 * main()
75 * {
76 * async_manager();
77 * }
78 *
79 * port_handler(ichandle, *icall)
80 * {
81 * if (want_refuse) {
82 * async_answer_0(ichandle, ELIMIT);
83 * return;
84 * }
85 * async_answer_0(ichandle, EOK);
86 *
87 * chandle = async_get_call(&call);
88 * somehow_handle_the_call(chandle, call);
89 * async_answer_2(chandle, 1, 2, 3);
90 *
91 * chandle = async_get_call(&call);
92 * ...
93 * }
94 *
95 */
96
97#define LIBC_ASYNC_C_
98#include <ipc/ipc.h>
99#include <async.h>
100#include "../private/async.h"
101#undef LIBC_ASYNC_C_
102
103#include <ipc/irq.h>
104#include <ipc/event.h>
105#include <futex.h>
106#include <fibril.h>
107#include <adt/hash_table.h>
108#include <adt/hash.h>
109#include <adt/list.h>
110#include <assert.h>
111#include <errno.h>
112#include <sys/time.h>
113#include <libarch/barrier.h>
114#include <stdbool.h>
115#include <stdlib.h>
116#include <mem.h>
117#include <stdlib.h>
118#include <macros.h>
119#include <as.h>
120#include <abi/mm/as.h>
121#include "../private/libc.h"
122
123/** Naming service session */
124async_sess_t *session_ns;
125
126/** Message data */
127typedef struct {
128 awaiter_t wdata;
129
130 /** If reply was received. */
131 bool done;
132
133 /** If the message / reply should be discarded on arrival. */
134 bool forget;
135
136 /** If already destroyed. */
137 bool destroyed;
138
139 /** Pointer to where the answer data is stored. */
140 ipc_call_t *dataptr;
141
142 errno_t retval;
143} amsg_t;
144
145static void to_event_initialize(to_event_t *to)
146{
147 struct timeval tv = { 0, 0 };
148
149 to->inlist = false;
150 to->occurred = false;
151 link_initialize(&to->link);
152 to->expires = tv;
153}
154
155static void wu_event_initialize(wu_event_t *wu)
156{
157 wu->inlist = false;
158 link_initialize(&wu->link);
159}
160
161void awaiter_initialize(awaiter_t *aw)
162{
163 aw->fid = 0;
164 aw->active = false;
165 to_event_initialize(&aw->to_event);
166 wu_event_initialize(&aw->wu_event);
167}
168
169static amsg_t *amsg_create(void)
170{
171 amsg_t *msg = malloc(sizeof(amsg_t));
172 if (msg) {
173 msg->done = false;
174 msg->forget = false;
175 msg->destroyed = false;
176 msg->dataptr = NULL;
177 msg->retval = EINVAL;
178 awaiter_initialize(&msg->wdata);
179 }
180
181 return msg;
182}
183
184static void amsg_destroy(amsg_t *msg)
185{
186 assert(!msg->destroyed);
187 msg->destroyed = true;
188 free(msg);
189}
190
191
192/** Mutex protecting inactive_exch_list and avail_phone_cv.
193 *
194 */
195static FIBRIL_MUTEX_INITIALIZE(async_sess_mutex);
196
197/** List of all currently inactive exchanges.
198 *
199 */
200static LIST_INITIALIZE(inactive_exch_list);
201
202/** Condition variable to wait for a phone to become available.
203 *
204 */
205static FIBRIL_CONDVAR_INITIALIZE(avail_phone_cv);
206
207/** Initialize the async framework.
208 *
209 */
210void __async_client_init(void)
211{
212 session_ns = (async_sess_t *) malloc(sizeof(async_sess_t));
213 if (session_ns == NULL)
214 abort();
215
216 session_ns->iface = 0;
217 session_ns->mgmt = EXCHANGE_ATOMIC;
218 session_ns->phone = PHONE_NS;
219 session_ns->arg1 = 0;
220 session_ns->arg2 = 0;
221 session_ns->arg3 = 0;
222
223 fibril_mutex_initialize(&session_ns->remote_state_mtx);
224 session_ns->remote_state_data = NULL;
225
226 list_initialize(&session_ns->exch_list);
227 fibril_mutex_initialize(&session_ns->mutex);
228 atomic_set(&session_ns->refcnt, 0);
229}
230
231/** Reply received callback.
232 *
233 * This function is called whenever a reply for an asynchronous message sent out
234 * by the asynchronous framework is received.
235 *
236 * Notify the fibril which is waiting for this message that it has arrived.
237 *
238 * @param arg Pointer to the asynchronous message record.
239 * @param retval Value returned in the answer.
240 * @param data Call data of the answer.
241 *
242 */
243static void reply_received(void *arg, errno_t retval, ipc_call_t *data)
244{
245 assert(arg);
246
247 futex_down(&async_futex);
248
249 amsg_t *msg = (amsg_t *) arg;
250 msg->retval = retval;
251
252 /* Copy data after futex_down, just in case the call was detached */
253 if ((msg->dataptr) && (data))
254 *msg->dataptr = *data;
255
256 write_barrier();
257
258 /* Remove message from timeout list */
259 if (msg->wdata.to_event.inlist)
260 list_remove(&msg->wdata.to_event.link);
261
262 msg->done = true;
263
264 if (msg->forget) {
265 assert(msg->wdata.active);
266 amsg_destroy(msg);
267 } else if (!msg->wdata.active) {
268 msg->wdata.active = true;
269 fibril_add_ready(msg->wdata.fid);
270 }
271
272 futex_up(&async_futex);
273}
274
275/** Send message and return id of the sent message.
276 *
277 * The return value can be used as input for async_wait() to wait for
278 * completion.
279 *
280 * @param exch Exchange for sending the message.
281 * @param imethod Service-defined interface and method.
282 * @param arg1 Service-defined payload argument.
283 * @param arg2 Service-defined payload argument.
284 * @param arg3 Service-defined payload argument.
285 * @param arg4 Service-defined payload argument.
286 * @param dataptr If non-NULL, storage where the reply data will be stored.
287 *
288 * @return Hash of the sent message or 0 on error.
289 *
290 */
291aid_t async_send_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
292 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
293{
294 if (exch == NULL)
295 return 0;
296
297 amsg_t *msg = amsg_create();
298 if (msg == NULL)
299 return 0;
300
301 msg->dataptr = dataptr;
302 msg->wdata.active = true;
303
304 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4, msg,
305 reply_received);
306
307 return (aid_t) msg;
308}
309
310/** Send message and return id of the sent message
311 *
312 * The return value can be used as input for async_wait() to wait for
313 * completion.
314 *
315 * @param exch Exchange for sending the message.
316 * @param imethod Service-defined interface and method.
317 * @param arg1 Service-defined payload argument.
318 * @param arg2 Service-defined payload argument.
319 * @param arg3 Service-defined payload argument.
320 * @param arg4 Service-defined payload argument.
321 * @param arg5 Service-defined payload argument.
322 * @param dataptr If non-NULL, storage where the reply data will be
323 * stored.
324 *
325 * @return Hash of the sent message or 0 on error.
326 *
327 */
328aid_t async_send_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
329 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
330 ipc_call_t *dataptr)
331{
332 if (exch == NULL)
333 return 0;
334
335 amsg_t *msg = amsg_create();
336 if (msg == NULL)
337 return 0;
338
339 msg->dataptr = dataptr;
340 msg->wdata.active = true;
341
342 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4, arg5,
343 msg, reply_received);
344
345 return (aid_t) msg;
346}
347
348/** Wait for a message sent by the async framework.
349 *
350 * @param amsgid Hash of the message to wait for.
351 * @param retval Pointer to storage where the retval of the answer will
352 * be stored.
353 *
354 */
355void async_wait_for(aid_t amsgid, errno_t *retval)
356{
357 assert(amsgid);
358
359 amsg_t *msg = (amsg_t *) amsgid;
360
361 futex_down(&async_futex);
362
363 assert(!msg->forget);
364 assert(!msg->destroyed);
365
366 if (msg->done) {
367 futex_up(&async_futex);
368 goto done;
369 }
370
371 msg->wdata.fid = fibril_get_id();
372 msg->wdata.active = false;
373 msg->wdata.to_event.inlist = false;
374
375 /* Leave the async_futex locked when entering this function */
376 fibril_switch(FIBRIL_TO_MANAGER);
377
378 /* Futex is up automatically after fibril_switch */
379
380done:
381 if (retval)
382 *retval = msg->retval;
383
384 amsg_destroy(msg);
385}
386
387/** Wait for a message sent by the async framework, timeout variant.
388 *
389 * If the wait times out, the caller may choose to either wait again by calling
390 * async_wait_for() or async_wait_timeout(), or forget the message via
391 * async_forget().
392 *
393 * @param amsgid Hash of the message to wait for.
394 * @param retval Pointer to storage where the retval of the answer will
395 * be stored.
396 * @param timeout Timeout in microseconds.
397 *
398 * @return Zero on success, ETIMEOUT if the timeout has expired.
399 *
400 */
401errno_t async_wait_timeout(aid_t amsgid, errno_t *retval, suseconds_t timeout)
402{
403 assert(amsgid);
404
405 amsg_t *msg = (amsg_t *) amsgid;
406
407 futex_down(&async_futex);
408
409 assert(!msg->forget);
410 assert(!msg->destroyed);
411
412 if (msg->done) {
413 futex_up(&async_futex);
414 goto done;
415 }
416
417 /*
418 * Negative timeout is converted to zero timeout to avoid
419 * using tv_add with negative augmenter.
420 */
421 if (timeout < 0)
422 timeout = 0;
423
424 getuptime(&msg->wdata.to_event.expires);
425 tv_add_diff(&msg->wdata.to_event.expires, timeout);
426
427 /*
428 * Current fibril is inserted as waiting regardless of the
429 * "size" of the timeout.
430 *
431 * Checking for msg->done and immediately bailing out when
432 * timeout == 0 would mean that the manager fibril would never
433 * run (consider single threaded program).
434 * Thus the IPC answer would be never retrieved from the kernel.
435 *
436 * Notice that the actual delay would be very small because we
437 * - switch to manager fibril
438 * - the manager sees expired timeout
439 * - and thus adds us back to ready queue
440 * - manager switches back to some ready fibril
441 * (prior it, it checks for incoming IPC).
442 *
443 */
444 msg->wdata.fid = fibril_get_id();
445 msg->wdata.active = false;
446 async_insert_timeout(&msg->wdata);
447
448 /* Leave the async_futex locked when entering this function */
449 fibril_switch(FIBRIL_TO_MANAGER);
450
451 /* Futex is up automatically after fibril_switch */
452
453 if (!msg->done)
454 return ETIMEOUT;
455
456done:
457 if (retval)
458 *retval = msg->retval;
459
460 amsg_destroy(msg);
461
462 return 0;
463}
464
465/** Discard the message / reply on arrival.
466 *
467 * The message will be marked to be discarded once the reply arrives in
468 * reply_received(). It is not allowed to call async_wait_for() or
469 * async_wait_timeout() on this message after a call to this function.
470 *
471 * @param amsgid Hash of the message to forget.
472 */
473void async_forget(aid_t amsgid)
474{
475 amsg_t *msg = (amsg_t *) amsgid;
476
477 assert(msg);
478 assert(!msg->forget);
479 assert(!msg->destroyed);
480
481 futex_down(&async_futex);
482
483 if (msg->done) {
484 amsg_destroy(msg);
485 } else {
486 msg->dataptr = NULL;
487 msg->forget = true;
488 }
489
490 futex_up(&async_futex);
491}
492
493/** Wait for specified time.
494 *
495 * The current fibril is suspended but the thread continues to execute.
496 *
497 * @param timeout Duration of the wait in microseconds.
498 *
499 */
500void async_usleep(suseconds_t timeout)
501{
502 awaiter_t awaiter;
503 awaiter_initialize(&awaiter);
504
505 awaiter.fid = fibril_get_id();
506
507 getuptime(&awaiter.to_event.expires);
508 tv_add_diff(&awaiter.to_event.expires, timeout);
509
510 futex_down(&async_futex);
511
512 async_insert_timeout(&awaiter);
513
514 /* Leave the async_futex locked when entering this function */
515 fibril_switch(FIBRIL_TO_MANAGER);
516
517 /* Futex is up automatically after fibril_switch() */
518}
519
520/** Delay execution for the specified number of seconds
521 *
522 * @param sec Number of seconds to sleep
523 */
524void async_sleep(unsigned int sec)
525{
526 /*
527 * Sleep in 1000 second steps to support
528 * full argument range
529 */
530
531 while (sec > 0) {
532 unsigned int period = (sec > 1000) ? 1000 : sec;
533
534 async_usleep(period * 1000000);
535 sec -= period;
536 }
537}
538
539/** Pseudo-synchronous message sending - fast version.
540 *
541 * Send message asynchronously and return only after the reply arrives.
542 *
543 * This function can only transfer 4 register payload arguments. For
544 * transferring more arguments, see the slower async_req_slow().
545 *
546 * @param exch Exchange for sending the message.
547 * @param imethod Interface and method of the call.
548 * @param arg1 Service-defined payload argument.
549 * @param arg2 Service-defined payload argument.
550 * @param arg3 Service-defined payload argument.
551 * @param arg4 Service-defined payload argument.
552 * @param r1 If non-NULL, storage for the 1st reply argument.
553 * @param r2 If non-NULL, storage for the 2nd reply argument.
554 * @param r3 If non-NULL, storage for the 3rd reply argument.
555 * @param r4 If non-NULL, storage for the 4th reply argument.
556 * @param r5 If non-NULL, storage for the 5th reply argument.
557 *
558 * @return Return code of the reply or an error code.
559 *
560 */
561errno_t async_req_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
562 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
563 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
564{
565 if (exch == NULL)
566 return ENOENT;
567
568 ipc_call_t result;
569 aid_t aid = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
570 &result);
571
572 errno_t rc;
573 async_wait_for(aid, &rc);
574
575 if (r1)
576 *r1 = IPC_GET_ARG1(result);
577
578 if (r2)
579 *r2 = IPC_GET_ARG2(result);
580
581 if (r3)
582 *r3 = IPC_GET_ARG3(result);
583
584 if (r4)
585 *r4 = IPC_GET_ARG4(result);
586
587 if (r5)
588 *r5 = IPC_GET_ARG5(result);
589
590 return rc;
591}
592
593/** Pseudo-synchronous message sending - slow version.
594 *
595 * Send message asynchronously and return only after the reply arrives.
596 *
597 * @param exch Exchange for sending the message.
598 * @param imethod Interface and method of the call.
599 * @param arg1 Service-defined payload argument.
600 * @param arg2 Service-defined payload argument.
601 * @param arg3 Service-defined payload argument.
602 * @param arg4 Service-defined payload argument.
603 * @param arg5 Service-defined payload argument.
604 * @param r1 If non-NULL, storage for the 1st reply argument.
605 * @param r2 If non-NULL, storage for the 2nd reply argument.
606 * @param r3 If non-NULL, storage for the 3rd reply argument.
607 * @param r4 If non-NULL, storage for the 4th reply argument.
608 * @param r5 If non-NULL, storage for the 5th reply argument.
609 *
610 * @return Return code of the reply or an error code.
611 *
612 */
613errno_t async_req_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
614 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
615 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
616{
617 if (exch == NULL)
618 return ENOENT;
619
620 ipc_call_t result;
621 aid_t aid = async_send_5(exch, imethod, arg1, arg2, arg3, arg4, arg5,
622 &result);
623
624 errno_t rc;
625 async_wait_for(aid, &rc);
626
627 if (r1)
628 *r1 = IPC_GET_ARG1(result);
629
630 if (r2)
631 *r2 = IPC_GET_ARG2(result);
632
633 if (r3)
634 *r3 = IPC_GET_ARG3(result);
635
636 if (r4)
637 *r4 = IPC_GET_ARG4(result);
638
639 if (r5)
640 *r5 = IPC_GET_ARG5(result);
641
642 return rc;
643}
644
645void async_msg_0(async_exch_t *exch, sysarg_t imethod)
646{
647 if (exch != NULL)
648 ipc_call_async_0(exch->phone, imethod, NULL, NULL);
649}
650
651void async_msg_1(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1)
652{
653 if (exch != NULL)
654 ipc_call_async_1(exch->phone, imethod, arg1, NULL, NULL);
655}
656
657void async_msg_2(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
658 sysarg_t arg2)
659{
660 if (exch != NULL)
661 ipc_call_async_2(exch->phone, imethod, arg1, arg2, NULL, NULL);
662}
663
664void async_msg_3(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
665 sysarg_t arg2, sysarg_t arg3)
666{
667 if (exch != NULL)
668 ipc_call_async_3(exch->phone, imethod, arg1, arg2, arg3, NULL,
669 NULL);
670}
671
672void async_msg_4(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
673 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
674{
675 if (exch != NULL)
676 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4,
677 NULL, NULL);
678}
679
680void async_msg_5(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
681 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
682{
683 if (exch != NULL)
684 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4,
685 arg5, NULL, NULL);
686}
687
688static errno_t async_connect_me_to_internal(cap_phone_handle_t phone,
689 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
690 cap_phone_handle_t *out_phone)
691{
692 ipc_call_t result;
693
694 // XXX: Workaround for GCC's inability to infer association between
695 // rc == EOK and *out_phone being assigned.
696 *out_phone = CAP_NIL;
697
698 amsg_t *msg = amsg_create();
699 if (!msg)
700 return ENOENT;
701
702 msg->dataptr = &result;
703 msg->wdata.active = true;
704
705 ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, arg4,
706 msg, reply_received);
707
708 errno_t rc;
709 async_wait_for((aid_t) msg, &rc);
710
711 if (rc != EOK)
712 return rc;
713
714 *out_phone = (cap_phone_handle_t) IPC_GET_ARG5(result);
715 return EOK;
716}
717
718/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
719 *
720 * Ask through for a new connection to some service.
721 *
722 * @param mgmt Exchange management style.
723 * @param exch Exchange for sending the message.
724 * @param arg1 User defined argument.
725 * @param arg2 User defined argument.
726 * @param arg3 User defined argument.
727 *
728 * @return New session on success or NULL on error.
729 *
730 */
731async_sess_t *async_connect_me_to(exch_mgmt_t mgmt, async_exch_t *exch,
732 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
733{
734 if (exch == NULL) {
735 errno = ENOENT;
736 return NULL;
737 }
738
739 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
740 if (sess == NULL) {
741 errno = ENOMEM;
742 return NULL;
743 }
744
745 cap_phone_handle_t phone;
746 errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
747 0, &phone);
748 if (rc != EOK) {
749 errno = rc;
750 free(sess);
751 return NULL;
752 }
753
754 sess->iface = 0;
755 sess->mgmt = mgmt;
756 sess->phone = phone;
757 sess->arg1 = arg1;
758 sess->arg2 = arg2;
759 sess->arg3 = arg3;
760
761 fibril_mutex_initialize(&sess->remote_state_mtx);
762 sess->remote_state_data = NULL;
763
764 list_initialize(&sess->exch_list);
765 fibril_mutex_initialize(&sess->mutex);
766 atomic_set(&sess->refcnt, 0);
767
768 return sess;
769}
770
771/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
772 *
773 * Ask through phone for a new connection to some service and block until
774 * success.
775 *
776 * @param exch Exchange for sending the message.
777 * @param iface Connection interface.
778 * @param arg2 User defined argument.
779 * @param arg3 User defined argument.
780 *
781 * @return New session on success or NULL on error.
782 *
783 */
784async_sess_t *async_connect_me_to_iface(async_exch_t *exch, iface_t iface,
785 sysarg_t arg2, sysarg_t arg3)
786{
787 if (exch == NULL) {
788 errno = ENOENT;
789 return NULL;
790 }
791
792 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
793 if (sess == NULL) {
794 errno = ENOMEM;
795 return NULL;
796 }
797
798 cap_phone_handle_t phone;
799 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
800 arg3, 0, &phone);
801 if (rc != EOK) {
802 errno = rc;
803 free(sess);
804 return NULL;
805 }
806
807 sess->iface = iface;
808 sess->phone = phone;
809 sess->arg1 = iface;
810 sess->arg2 = arg2;
811 sess->arg3 = arg3;
812
813 fibril_mutex_initialize(&sess->remote_state_mtx);
814 sess->remote_state_data = NULL;
815
816 list_initialize(&sess->exch_list);
817 fibril_mutex_initialize(&sess->mutex);
818 atomic_set(&sess->refcnt, 0);
819
820 return sess;
821}
822
823/** Set arguments for new connections.
824 *
825 * FIXME This is an ugly hack to work around the problem that parallel
826 * exchanges are implemented using parallel connections. When we create
827 * a callback session, the framework does not know arguments for the new
828 * connections.
829 *
830 * The proper solution seems to be to implement parallel exchanges using
831 * tagging.
832 */
833void async_sess_args_set(async_sess_t *sess, sysarg_t arg1, sysarg_t arg2,
834 sysarg_t arg3)
835{
836 sess->arg1 = arg1;
837 sess->arg2 = arg2;
838 sess->arg3 = arg3;
839}
840
841/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
842 *
843 * Ask through phone for a new connection to some service and block until
844 * success.
845 *
846 * @param mgmt Exchange management style.
847 * @param exch Exchange for sending the message.
848 * @param arg1 User defined argument.
849 * @param arg2 User defined argument.
850 * @param arg3 User defined argument.
851 *
852 * @return New session on success or NULL on error.
853 *
854 */
855async_sess_t *async_connect_me_to_blocking(exch_mgmt_t mgmt, async_exch_t *exch,
856 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
857{
858 if (exch == NULL) {
859 errno = ENOENT;
860 return NULL;
861 }
862
863 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
864 if (sess == NULL) {
865 errno = ENOMEM;
866 return NULL;
867 }
868
869 cap_phone_handle_t phone;
870 errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
871 IPC_FLAG_BLOCKING, &phone);
872
873 if (rc != EOK) {
874 errno = rc;
875 free(sess);
876 return NULL;
877 }
878
879 sess->iface = 0;
880 sess->mgmt = mgmt;
881 sess->phone = phone;
882 sess->arg1 = arg1;
883 sess->arg2 = arg2;
884 sess->arg3 = arg3;
885
886 fibril_mutex_initialize(&sess->remote_state_mtx);
887 sess->remote_state_data = NULL;
888
889 list_initialize(&sess->exch_list);
890 fibril_mutex_initialize(&sess->mutex);
891 atomic_set(&sess->refcnt, 0);
892
893 return sess;
894}
895
896/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
897 *
898 * Ask through phone for a new connection to some service and block until
899 * success.
900 *
901 * @param exch Exchange for sending the message.
902 * @param iface Connection interface.
903 * @param arg2 User defined argument.
904 * @param arg3 User defined argument.
905 *
906 * @return New session on success or NULL on error.
907 *
908 */
909async_sess_t *async_connect_me_to_blocking_iface(async_exch_t *exch, iface_t iface,
910 sysarg_t arg2, sysarg_t arg3)
911{
912 if (exch == NULL) {
913 errno = ENOENT;
914 return NULL;
915 }
916
917 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
918 if (sess == NULL) {
919 errno = ENOMEM;
920 return NULL;
921 }
922
923 cap_phone_handle_t phone;
924 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
925 arg3, IPC_FLAG_BLOCKING, &phone);
926 if (rc != EOK) {
927 errno = rc;
928 free(sess);
929 return NULL;
930 }
931
932 sess->iface = iface;
933 sess->phone = phone;
934 sess->arg1 = iface;
935 sess->arg2 = arg2;
936 sess->arg3 = arg3;
937
938 fibril_mutex_initialize(&sess->remote_state_mtx);
939 sess->remote_state_data = NULL;
940
941 list_initialize(&sess->exch_list);
942 fibril_mutex_initialize(&sess->mutex);
943 atomic_set(&sess->refcnt, 0);
944
945 return sess;
946}
947
948/** Connect to a task specified by id.
949 *
950 */
951async_sess_t *async_connect_kbox(task_id_t id)
952{
953 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
954 if (sess == NULL) {
955 errno = ENOMEM;
956 return NULL;
957 }
958
959 cap_phone_handle_t phone;
960 errno_t rc = ipc_connect_kbox(id, &phone);
961 if (rc != EOK) {
962 errno = rc;
963 free(sess);
964 return NULL;
965 }
966
967 sess->iface = 0;
968 sess->mgmt = EXCHANGE_ATOMIC;
969 sess->phone = phone;
970 sess->arg1 = 0;
971 sess->arg2 = 0;
972 sess->arg3 = 0;
973
974 fibril_mutex_initialize(&sess->remote_state_mtx);
975 sess->remote_state_data = NULL;
976
977 list_initialize(&sess->exch_list);
978 fibril_mutex_initialize(&sess->mutex);
979 atomic_set(&sess->refcnt, 0);
980
981 return sess;
982}
983
984static errno_t async_hangup_internal(cap_phone_handle_t phone)
985{
986 return ipc_hangup(phone);
987}
988
989/** Wrapper for ipc_hangup.
990 *
991 * @param sess Session to hung up.
992 *
993 * @return Zero on success or an error code.
994 *
995 */
996errno_t async_hangup(async_sess_t *sess)
997{
998 async_exch_t *exch;
999
1000 assert(sess);
1001
1002 if (atomic_get(&sess->refcnt) > 0)
1003 return EBUSY;
1004
1005 fibril_mutex_lock(&async_sess_mutex);
1006
1007 errno_t rc = async_hangup_internal(sess->phone);
1008
1009 while (!list_empty(&sess->exch_list)) {
1010 exch = (async_exch_t *)
1011 list_get_instance(list_first(&sess->exch_list),
1012 async_exch_t, sess_link);
1013
1014 list_remove(&exch->sess_link);
1015 list_remove(&exch->global_link);
1016 async_hangup_internal(exch->phone);
1017 free(exch);
1018 }
1019
1020 free(sess);
1021
1022 fibril_mutex_unlock(&async_sess_mutex);
1023
1024 return rc;
1025}
1026
1027/** Start new exchange in a session.
1028 *
1029 * @param session Session.
1030 *
1031 * @return New exchange or NULL on error.
1032 *
1033 */
1034async_exch_t *async_exchange_begin(async_sess_t *sess)
1035{
1036 if (sess == NULL)
1037 return NULL;
1038
1039 exch_mgmt_t mgmt = sess->mgmt;
1040 if (sess->iface != 0)
1041 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
1042
1043 async_exch_t *exch = NULL;
1044
1045 fibril_mutex_lock(&async_sess_mutex);
1046
1047 if (!list_empty(&sess->exch_list)) {
1048 /*
1049 * There are inactive exchanges in the session.
1050 */
1051 exch = (async_exch_t *)
1052 list_get_instance(list_first(&sess->exch_list),
1053 async_exch_t, sess_link);
1054
1055 list_remove(&exch->sess_link);
1056 list_remove(&exch->global_link);
1057 } else {
1058 /*
1059 * There are no available exchanges in the session.
1060 */
1061
1062 if ((mgmt == EXCHANGE_ATOMIC) ||
1063 (mgmt == EXCHANGE_SERIALIZE)) {
1064 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
1065 if (exch != NULL) {
1066 link_initialize(&exch->sess_link);
1067 link_initialize(&exch->global_link);
1068 exch->sess = sess;
1069 exch->phone = sess->phone;
1070 }
1071 } else if (mgmt == EXCHANGE_PARALLEL) {
1072 cap_phone_handle_t phone;
1073 errno_t rc;
1074
1075 retry:
1076 /*
1077 * Make a one-time attempt to connect a new data phone.
1078 */
1079 rc = async_connect_me_to_internal(sess->phone, sess->arg1,
1080 sess->arg2, sess->arg3, 0, &phone);
1081 if (rc == EOK) {
1082 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
1083 if (exch != NULL) {
1084 link_initialize(&exch->sess_link);
1085 link_initialize(&exch->global_link);
1086 exch->sess = sess;
1087 exch->phone = phone;
1088 } else
1089 async_hangup_internal(phone);
1090 } else if (!list_empty(&inactive_exch_list)) {
1091 /*
1092 * We did not manage to connect a new phone. But we
1093 * can try to close some of the currently inactive
1094 * connections in other sessions and try again.
1095 */
1096 exch = (async_exch_t *)
1097 list_get_instance(list_first(&inactive_exch_list),
1098 async_exch_t, global_link);
1099
1100 list_remove(&exch->sess_link);
1101 list_remove(&exch->global_link);
1102 async_hangup_internal(exch->phone);
1103 free(exch);
1104 goto retry;
1105 } else {
1106 /*
1107 * Wait for a phone to become available.
1108 */
1109 fibril_condvar_wait(&avail_phone_cv, &async_sess_mutex);
1110 goto retry;
1111 }
1112 }
1113 }
1114
1115 fibril_mutex_unlock(&async_sess_mutex);
1116
1117 if (exch != NULL) {
1118 atomic_inc(&sess->refcnt);
1119
1120 if (mgmt == EXCHANGE_SERIALIZE)
1121 fibril_mutex_lock(&sess->mutex);
1122 }
1123
1124 return exch;
1125}
1126
1127/** Finish an exchange.
1128 *
1129 * @param exch Exchange to finish.
1130 *
1131 */
1132void async_exchange_end(async_exch_t *exch)
1133{
1134 if (exch == NULL)
1135 return;
1136
1137 async_sess_t *sess = exch->sess;
1138 assert(sess != NULL);
1139
1140 exch_mgmt_t mgmt = sess->mgmt;
1141 if (sess->iface != 0)
1142 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
1143
1144 atomic_dec(&sess->refcnt);
1145
1146 if (mgmt == EXCHANGE_SERIALIZE)
1147 fibril_mutex_unlock(&sess->mutex);
1148
1149 fibril_mutex_lock(&async_sess_mutex);
1150
1151 list_append(&exch->sess_link, &sess->exch_list);
1152 list_append(&exch->global_link, &inactive_exch_list);
1153 fibril_condvar_signal(&avail_phone_cv);
1154
1155 fibril_mutex_unlock(&async_sess_mutex);
1156}
1157
1158/** Wrapper for IPC_M_SHARE_IN calls using the async framework.
1159 *
1160 * @param exch Exchange for sending the message.
1161 * @param size Size of the destination address space area.
1162 * @param arg User defined argument.
1163 * @param flags Storage for the received flags. Can be NULL.
1164 * @param dst Address of the storage for the destination address space area
1165 * base address. Cannot be NULL.
1166 *
1167 * @return Zero on success or an error code from errno.h.
1168 *
1169 */
1170errno_t async_share_in_start(async_exch_t *exch, size_t size, sysarg_t arg,
1171 unsigned int *flags, void **dst)
1172{
1173 if (exch == NULL)
1174 return ENOENT;
1175
1176 sysarg_t _flags = 0;
1177 sysarg_t _dst = (sysarg_t) -1;
1178 errno_t res = async_req_2_4(exch, IPC_M_SHARE_IN, (sysarg_t) size,
1179 arg, NULL, &_flags, NULL, &_dst);
1180
1181 if (flags)
1182 *flags = (unsigned int) _flags;
1183
1184 *dst = (void *) _dst;
1185 return res;
1186}
1187
1188/** Wrapper for IPC_M_SHARE_OUT calls using the async framework.
1189 *
1190 * @param exch Exchange for sending the message.
1191 * @param src Source address space area base address.
1192 * @param flags Flags to be used for sharing. Bits can be only cleared.
1193 *
1194 * @return Zero on success or an error code from errno.h.
1195 *
1196 */
1197errno_t async_share_out_start(async_exch_t *exch, void *src, unsigned int flags)
1198{
1199 if (exch == NULL)
1200 return ENOENT;
1201
1202 return async_req_3_0(exch, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
1203 (sysarg_t) flags);
1204}
1205
1206/** Start IPC_M_DATA_READ using the async framework.
1207 *
1208 * @param exch Exchange for sending the message.
1209 * @param dst Address of the beginning of the destination buffer.
1210 * @param size Size of the destination buffer (in bytes).
1211 * @param dataptr Storage of call data (arg 2 holds actual data size).
1212 *
1213 * @return Hash of the sent message or 0 on error.
1214 *
1215 */
1216aid_t async_data_read(async_exch_t *exch, void *dst, size_t size,
1217 ipc_call_t *dataptr)
1218{
1219 return async_send_2(exch, IPC_M_DATA_READ, (sysarg_t) dst,
1220 (sysarg_t) size, dataptr);
1221}
1222
1223/** Wrapper for IPC_M_DATA_READ calls using the async framework.
1224 *
1225 * @param exch Exchange for sending the message.
1226 * @param dst Address of the beginning of the destination buffer.
1227 * @param size Size of the destination buffer.
1228 *
1229 * @return Zero on success or an error code from errno.h.
1230 *
1231 */
1232errno_t async_data_read_start(async_exch_t *exch, void *dst, size_t size)
1233{
1234 if (exch == NULL)
1235 return ENOENT;
1236
1237 return async_req_2_0(exch, IPC_M_DATA_READ, (sysarg_t) dst,
1238 (sysarg_t) size);
1239}
1240
1241/** Wrapper for IPC_M_DATA_WRITE calls using the async framework.
1242 *
1243 * @param exch Exchange for sending the message.
1244 * @param src Address of the beginning of the source buffer.
1245 * @param size Size of the source buffer.
1246 *
1247 * @return Zero on success or an error code from errno.h.
1248 *
1249 */
1250errno_t async_data_write_start(async_exch_t *exch, const void *src, size_t size)
1251{
1252 if (exch == NULL)
1253 return ENOENT;
1254
1255 return async_req_2_0(exch, IPC_M_DATA_WRITE, (sysarg_t) src,
1256 (sysarg_t) size);
1257}
1258
1259errno_t async_state_change_start(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
1260 sysarg_t arg3, async_exch_t *other_exch)
1261{
1262 return async_req_5_0(exch, IPC_M_STATE_CHANGE_AUTHORIZE,
1263 arg1, arg2, arg3, 0, CAP_HANDLE_RAW(other_exch->phone));
1264}
1265
1266/** Lock and get session remote state
1267 *
1268 * Lock and get the local replica of the remote state
1269 * in stateful sessions. The call should be paired
1270 * with async_remote_state_release*().
1271 *
1272 * @param[in] sess Stateful session.
1273 *
1274 * @return Local replica of the remote state.
1275 *
1276 */
1277void *async_remote_state_acquire(async_sess_t *sess)
1278{
1279 fibril_mutex_lock(&sess->remote_state_mtx);
1280 return sess->remote_state_data;
1281}
1282
1283/** Update the session remote state
1284 *
1285 * Update the local replica of the remote state
1286 * in stateful sessions. The remote state must
1287 * be already locked.
1288 *
1289 * @param[in] sess Stateful session.
1290 * @param[in] state New local replica of the remote state.
1291 *
1292 */
1293void async_remote_state_update(async_sess_t *sess, void *state)
1294{
1295 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
1296 sess->remote_state_data = state;
1297}
1298
1299/** Release the session remote state
1300 *
1301 * Unlock the local replica of the remote state
1302 * in stateful sessions.
1303 *
1304 * @param[in] sess Stateful session.
1305 *
1306 */
1307void async_remote_state_release(async_sess_t *sess)
1308{
1309 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
1310
1311 fibril_mutex_unlock(&sess->remote_state_mtx);
1312}
1313
1314/** Release the session remote state and end an exchange
1315 *
1316 * Unlock the local replica of the remote state
1317 * in stateful sessions. This is convenience function
1318 * which gets the session pointer from the exchange
1319 * and also ends the exchange.
1320 *
1321 * @param[in] exch Stateful session's exchange.
1322 *
1323 */
1324void async_remote_state_release_exchange(async_exch_t *exch)
1325{
1326 if (exch == NULL)
1327 return;
1328
1329 async_sess_t *sess = exch->sess;
1330 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
1331
1332 async_exchange_end(exch);
1333 fibril_mutex_unlock(&sess->remote_state_mtx);
1334}
1335
1336void *async_as_area_create(void *base, size_t size, unsigned int flags,
1337 async_sess_t *pager, sysarg_t id1, sysarg_t id2, sysarg_t id3)
1338{
1339 as_area_pager_info_t pager_info = {
1340 .pager = pager->phone,
1341 .id1 = id1,
1342 .id2 = id2,
1343 .id3 = id3
1344 };
1345 return as_area_create(base, size, flags, &pager_info);
1346}
1347
1348void async_call_begin(async_call_t *call, async_sess_t *sess, sysarg_t imethod,
1349 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1350{
1351 assert(call);
1352 memset(call, 0, sizeof(*call));
1353 list_initialize(&call->fragments);
1354
1355 if (!sess) {
1356 call->rc = ENOENT;
1357 return;
1358 }
1359
1360 call->exch = async_exchange_begin(sess);
1361 if (!call->exch) {
1362 call->rc = ENOMEM;
1363 return;
1364 }
1365
1366 async_call_method(call, &call->initial, imethod,
1367 arg1, arg2, arg3, arg4);
1368}
1369
1370static suseconds_t time_until(const struct timeval *t)
1371{
1372 struct timeval tv;
1373 getuptime(&tv);
1374 if (tv_gteq(&tv, t))
1375 return 1;
1376
1377 return tv_sub_diff(t, &tv);
1378}
1379
1380static errno_t async_call_finish_internal(async_call_t *call, const struct timeval *expires)
1381{
1382 errno_t rc;
1383
1384 /* Wait for all the fragments. */
1385 while (!list_empty(&call->fragments)) {
1386 link_t *tmp = list_first(&call->fragments);
1387 async_call_data_t *frag =
1388 list_get_instance(tmp, async_call_data_t, link);
1389
1390 if (expires) {
1391 errno_t trc = async_wait_timeout(frag->msgid, &rc,
1392 time_until(expires));
1393 if (trc != EOK)
1394 return trc;
1395 } else {
1396 async_wait_for(frag->msgid, &rc);
1397 }
1398
1399 list_remove(tmp);
1400
1401 if (rc != EOK)
1402 return rc;
1403
1404 if (frag->finalizer) {
1405 rc = frag->finalizer(frag);
1406 if (rc != EOK)
1407 return rc;
1408 }
1409 }
1410
1411 return EOK;
1412}
1413
1414errno_t async_call_finish_timeout(async_call_t *call, const struct timeval *expires)
1415{
1416 assert(call);
1417
1418 if (call->rc)
1419 return call->rc;
1420
1421 if (call->exch) {
1422 async_exchange_end(call->exch);
1423 call->exch = NULL;
1424 }
1425
1426 errno_t rc = async_call_finish_internal(call, expires);
1427 if (rc == ETIMEOUT)
1428 return rc;
1429
1430 /* If one fails, abort the call. */
1431 if (rc != EOK)
1432 async_call_abort(call);
1433
1434 assert(list_empty(&call->fragments));
1435 call->rc = rc;
1436 return rc;
1437}
1438
1439// Ends the call, and waits for all in-flight fragments to finish.
1440errno_t async_call_finish(async_call_t *call)
1441{
1442 return async_call_finish_timeout(call, NULL);
1443}
1444
1445// Aborts the call. After this function returns, auxiliary structures
1446// and buffers are safe to deallocate.
1447extern void async_call_abort(async_call_t *call)
1448{
1449 // FIXME: Proper abort needs kernel support. A system call should
1450 // clean up bookkeeping structures in the kernel and notify
1451 // the server of the abort as well.
1452
1453 // Currently, we just wait, which is less than ideal,
1454 // but at the same time, nothing in HelenOS currently
1455 // benefits from timeouts.
1456
1457 assert(call);
1458
1459 if (call->exch) {
1460 async_exchange_end(call->exch);
1461 call->exch = NULL;
1462 }
1463
1464 /* Wait for all the fragments. */
1465 while (!list_empty(&call->fragments)) {
1466 // TODO: abort instead of waiting
1467 (void) async_call_finish_internal(call, NULL);
1468 }
1469
1470 assert(list_empty(&call->fragments));
1471}
1472
1473// Waits for all in-flight fragments to finish, but doesn't end the call.
1474errno_t async_call_wait(async_call_t *call)
1475{
1476 return async_call_wait_timeout(call, NULL);
1477}
1478
1479errno_t async_call_wait_timeout(async_call_t *call, const struct timeval *expires)
1480{
1481 assert(call);
1482
1483 if (call->rc)
1484 return call->rc;
1485
1486 /* Wait for all the fragments except the initial one. */
1487 assert(list_first(&call->fragments) == &call->initial.link);
1488 list_remove(&call->initial.link);
1489
1490 errno_t rc = async_call_finish_internal(call, expires);
1491 list_prepend(&call->initial.link, &call->fragments);
1492
1493 if (rc == ETIMEOUT)
1494 return rc;
1495
1496 /* If one fails, abort the call. */
1497 if (rc != EOK)
1498 async_call_abort(call);
1499
1500 call->rc = rc;
1501 return rc;
1502}
1503
1504void async_call_method_with_finalizer(async_call_t *call,
1505 async_call_data_t *data, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2,
1506 sysarg_t arg3, sysarg_t arg4, async_call_finalizer_t finalizer)
1507{
1508 assert(call);
1509 assert(data);
1510 data->finalizer = finalizer;
1511
1512 if (call->rc)
1513 return;
1514
1515 if (!call->exch) {
1516 call->rc = ENOENT;
1517 return;
1518 }
1519
1520 data->msgid = async_send_fast(call->exch, imethod,
1521 arg1, arg2, arg3, arg4, &data->answer);
1522 if (!data->msgid) {
1523 async_call_abort(call);
1524 call->rc = ENOMEM;
1525 }
1526
1527 list_append(&data->link, &call->fragments);
1528}
1529
1530void async_call_method(async_call_t *call, async_call_data_t *data,
1531 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1532{
1533 assert(call);
1534 assert(data);
1535 memset(data, 0, sizeof(*data));
1536
1537 async_call_method_with_finalizer(call, data, imethod,
1538 arg1, arg2, arg3, arg4, NULL);
1539}
1540
1541static errno_t call_read_write_finalizer(async_call_data_t *data)
1542{
1543 size_t *sz = data->arg1;
1544 if (sz)
1545 *sz = IPC_GET_ARG2(data->answer);
1546 return EOK;
1547}
1548
1549void async_call_read(async_call_t *call, async_call_data_t *data,
1550 void *dst, size_t size, size_t *nread)
1551{
1552 assert(call);
1553 assert(data);
1554 memset(data, 0, sizeof(*data));
1555
1556 data->arg1 = nread;
1557
1558 async_call_method_with_finalizer(call, data,
1559 IPC_M_DATA_READ, (sysarg_t) dst, (sysarg_t) size, 0, 0,
1560 call_read_write_finalizer);
1561}
1562
1563/**
1564 * After the call is successfully finished,
1565 * `IPC_GET_ARG2(&data->answer)` holds the actual number of bytes written.
1566 */
1567void async_call_write(async_call_t *call, async_call_data_t *data,
1568 const void *src, size_t size, size_t *nwritten)
1569{
1570 assert(call);
1571 assert(data);
1572 memset(data, 0, sizeof(*data));
1573
1574 data->arg1 = nwritten;
1575
1576 async_call_method_with_finalizer(call, data,
1577 IPC_M_DATA_WRITE, (sysarg_t) src, (sysarg_t) size, 0, 0,
1578 call_read_write_finalizer);
1579}
1580
1581static errno_t call_share_in_finalizer(async_call_data_t *data)
1582{
1583 unsigned int *flags = data->arg1;
1584 void **dst = data->arg2;
1585
1586 if (flags)
1587 *flags = IPC_GET_ARG2(data->answer);
1588
1589 if (dst)
1590 *dst = (void *) IPC_GET_ARG4(data->answer);
1591
1592 return EOK;
1593}
1594
1595void async_call_share_in(async_call_t *call, async_call_data_t *data,
1596 size_t size, sysarg_t arg, unsigned int *flags, void **dst)
1597{
1598 assert(call);
1599 assert(data);
1600 memset(data, 0, sizeof(*data));
1601
1602 data->arg1 = flags;
1603 data->arg2 = dst;
1604
1605 async_call_method_with_finalizer(call, data,
1606 IPC_M_SHARE_IN, (sysarg_t) size, (sysarg_t) arg, 0, 0,
1607 call_share_in_finalizer);
1608}
1609
1610void async_call_share_out(async_call_t *call, async_call_data_t *data,
1611 void *src, unsigned int flags)
1612{
1613 async_call_method(call, data,
1614 IPC_M_SHARE_OUT, (sysarg_t) src, 0, (sysarg_t) flags, 0);
1615}
1616
1617void async_call_connect_to_me(async_call_t *call, async_call_data_t *data,
1618 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
1619{
1620 async_call_method(call, data, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3, 0);
1621}
1622
1623// TODO: connect me to, connect to me, vfs handle, etc.
1624
1625/** @}
1626 */
Note: See TracBrowser for help on using the repository browser.