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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since efa3136 was efa3136, checked in by Martin Decky <martin@…>, 7 years ago

cstyle (no change in functionality)

  • Property mode set to 100644
File size: 26.8 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#include "../private/ns.h"
102#undef LIBC_ASYNC_C_
103
104#include <ipc/irq.h>
105#include <ipc/event.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#include "../private/fibril.h"
123
124/** Naming service session */
125async_sess_t session_ns;
126
127/** Message data */
128typedef struct {
129 fibril_event_t received;
130
131 /** If reply was received. */
132 bool done;
133
134 /** If the message / reply should be discarded on arrival. */
135 bool forget;
136
137 /** Pointer to where the answer data is stored. */
138 ipc_call_t *dataptr;
139
140 errno_t retval;
141} amsg_t;
142
143static amsg_t *amsg_create(void)
144{
145 return calloc(1, sizeof(amsg_t));
146}
147
148static void amsg_destroy(amsg_t *msg)
149{
150 free(msg);
151}
152
153/** Mutex protecting inactive_exch_list and avail_phone_cv.
154 *
155 */
156static FIBRIL_MUTEX_INITIALIZE(async_sess_mutex);
157
158/** List of all currently inactive exchanges.
159 *
160 */
161static LIST_INITIALIZE(inactive_exch_list);
162
163/** Condition variable to wait for a phone to become available.
164 *
165 */
166static FIBRIL_CONDVAR_INITIALIZE(avail_phone_cv);
167
168/** Initialize the async framework.
169 *
170 */
171void __async_client_init(void)
172{
173 session_ns.iface = 0;
174 session_ns.mgmt = EXCHANGE_ATOMIC;
175 session_ns.phone = PHONE_NS;
176 session_ns.arg1 = 0;
177 session_ns.arg2 = 0;
178 session_ns.arg3 = 0;
179
180 fibril_mutex_initialize(&session_ns.remote_state_mtx);
181 session_ns.remote_state_data = NULL;
182
183 list_initialize(&session_ns.exch_list);
184 fibril_mutex_initialize(&session_ns.mutex);
185 atomic_set(&session_ns.refcnt, 0);
186}
187
188/** Reply received callback.
189 *
190 * This function is called whenever a reply for an asynchronous message sent out
191 * by the asynchronous framework is received.
192 *
193 * Notify the fibril which is waiting for this message that it has arrived.
194 *
195 * @param arg Pointer to the asynchronous message record.
196 * @param retval Value returned in the answer.
197 * @param data Call data of the answer.
198 *
199 */
200void async_reply_received(ipc_call_t *data)
201{
202 amsg_t *msg = data->label;
203 if (!msg)
204 return;
205
206 futex_lock(&async_futex);
207
208 msg->retval = IPC_GET_RETVAL(*data);
209
210 /* Copy data inside lock, just in case the call was detached */
211 if ((msg->dataptr) && (data))
212 *msg->dataptr = *data;
213
214 msg->done = true;
215
216 if (msg->forget) {
217 amsg_destroy(msg);
218 } else {
219 fibril_notify(&msg->received);
220 }
221
222 futex_unlock(&async_futex);
223}
224
225/** Send message and return id of the sent message.
226 *
227 * The return value can be used as input for async_wait() to wait for
228 * completion.
229 *
230 * @param exch Exchange for sending the message.
231 * @param imethod Service-defined interface and method.
232 * @param arg1 Service-defined payload argument.
233 * @param arg2 Service-defined payload argument.
234 * @param arg3 Service-defined payload argument.
235 * @param arg4 Service-defined payload argument.
236 * @param dataptr If non-NULL, storage where the reply data will be stored.
237 *
238 * @return Hash of the sent message or 0 on error.
239 *
240 */
241aid_t async_send_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
242 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
243{
244 if (exch == NULL)
245 return 0;
246
247 amsg_t *msg = amsg_create();
248 if (msg == NULL)
249 return 0;
250
251 msg->dataptr = dataptr;
252
253 errno_t rc = ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3,
254 arg4, msg);
255 if (rc != EOK) {
256 msg->retval = rc;
257 msg->done = true;
258 }
259
260 return (aid_t) msg;
261}
262
263/** Send message and return id of the sent message
264 *
265 * The return value can be used as input for async_wait() to wait for
266 * completion.
267 *
268 * @param exch Exchange for sending the message.
269 * @param imethod Service-defined interface and method.
270 * @param arg1 Service-defined payload argument.
271 * @param arg2 Service-defined payload argument.
272 * @param arg3 Service-defined payload argument.
273 * @param arg4 Service-defined payload argument.
274 * @param arg5 Service-defined payload argument.
275 * @param dataptr If non-NULL, storage where the reply data will be
276 * stored.
277 *
278 * @return Hash of the sent message or 0 on error.
279 *
280 */
281aid_t async_send_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
282 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
283 ipc_call_t *dataptr)
284{
285 if (exch == NULL)
286 return 0;
287
288 amsg_t *msg = amsg_create();
289 if (msg == NULL)
290 return 0;
291
292 msg->dataptr = dataptr;
293
294 errno_t rc = ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3,
295 arg4, arg5, msg);
296 if (rc != EOK) {
297 msg->retval = rc;
298 msg->done = true;
299 }
300
301 return (aid_t) msg;
302}
303
304/** Wait for a message sent by the async framework.
305 *
306 * @param amsgid Hash of the message to wait for.
307 * @param retval Pointer to storage where the retval of the answer will
308 * be stored.
309 *
310 */
311void async_wait_for(aid_t amsgid, errno_t *retval)
312{
313 if (amsgid == 0) {
314 if (retval)
315 *retval = ENOMEM;
316 return;
317 }
318
319 amsg_t *msg = (amsg_t *) amsgid;
320 fibril_wait_for(&msg->received);
321
322 if (retval)
323 *retval = msg->retval;
324
325 amsg_destroy(msg);
326}
327
328/** Wait for a message sent by the async framework, timeout variant.
329 *
330 * If the wait times out, the caller may choose to either wait again by calling
331 * async_wait_for() or async_wait_timeout(), or forget the message via
332 * async_forget().
333 *
334 * @param amsgid Hash of the message to wait for.
335 * @param retval Pointer to storage where the retval of the answer will
336 * be stored.
337 * @param timeout Timeout in microseconds.
338 *
339 * @return Zero on success, ETIMEOUT if the timeout has expired.
340 *
341 */
342errno_t async_wait_timeout(aid_t amsgid, errno_t *retval, suseconds_t timeout)
343{
344 if (amsgid == 0) {
345 if (retval)
346 *retval = ENOMEM;
347 return EOK;
348 }
349
350 amsg_t *msg = (amsg_t *) amsgid;
351
352 /*
353 * Negative timeout is converted to zero timeout to avoid
354 * using tv_add with negative augmenter.
355 */
356 if (timeout < 0)
357 timeout = 0;
358
359 struct timeval expires;
360 getuptime(&expires);
361 tv_add_diff(&expires, timeout);
362
363 errno_t rc = fibril_wait_timeout(&msg->received, &expires);
364 if (rc != EOK)
365 return rc;
366
367 if (retval)
368 *retval = msg->retval;
369
370 amsg_destroy(msg);
371
372 return EOK;
373}
374
375/** Discard the message / reply on arrival.
376 *
377 * The message will be marked to be discarded once the reply arrives in
378 * reply_received(). It is not allowed to call async_wait_for() or
379 * async_wait_timeout() on this message after a call to this function.
380 *
381 * @param amsgid Hash of the message to forget.
382 */
383void async_forget(aid_t amsgid)
384{
385 if (amsgid == 0)
386 return;
387
388 amsg_t *msg = (amsg_t *) amsgid;
389
390 assert(!msg->forget);
391
392 futex_lock(&async_futex);
393
394 if (msg->done) {
395 amsg_destroy(msg);
396 } else {
397 msg->dataptr = NULL;
398 msg->forget = true;
399 }
400
401 futex_unlock(&async_futex);
402}
403
404/** Pseudo-synchronous message sending - fast version.
405 *
406 * Send message asynchronously and return only after the reply arrives.
407 *
408 * This function can only transfer 4 register payload arguments. For
409 * transferring more arguments, see the slower async_req_slow().
410 *
411 * @param exch Exchange for sending the message.
412 * @param imethod Interface and method of the call.
413 * @param arg1 Service-defined payload argument.
414 * @param arg2 Service-defined payload argument.
415 * @param arg3 Service-defined payload argument.
416 * @param arg4 Service-defined payload argument.
417 * @param r1 If non-NULL, storage for the 1st reply argument.
418 * @param r2 If non-NULL, storage for the 2nd reply argument.
419 * @param r3 If non-NULL, storage for the 3rd reply argument.
420 * @param r4 If non-NULL, storage for the 4th reply argument.
421 * @param r5 If non-NULL, storage for the 5th reply argument.
422 *
423 * @return Return code of the reply or an error code.
424 *
425 */
426errno_t async_req_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
427 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
428 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
429{
430 if (exch == NULL)
431 return ENOENT;
432
433 ipc_call_t result;
434 aid_t aid = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
435 &result);
436
437 errno_t rc;
438 async_wait_for(aid, &rc);
439
440 if (r1)
441 *r1 = IPC_GET_ARG1(result);
442
443 if (r2)
444 *r2 = IPC_GET_ARG2(result);
445
446 if (r3)
447 *r3 = IPC_GET_ARG3(result);
448
449 if (r4)
450 *r4 = IPC_GET_ARG4(result);
451
452 if (r5)
453 *r5 = IPC_GET_ARG5(result);
454
455 return rc;
456}
457
458/** Pseudo-synchronous message sending - slow version.
459 *
460 * Send message asynchronously and return only after the reply arrives.
461 *
462 * @param exch Exchange for sending the message.
463 * @param imethod Interface and method of the call.
464 * @param arg1 Service-defined payload argument.
465 * @param arg2 Service-defined payload argument.
466 * @param arg3 Service-defined payload argument.
467 * @param arg4 Service-defined payload argument.
468 * @param arg5 Service-defined payload argument.
469 * @param r1 If non-NULL, storage for the 1st reply argument.
470 * @param r2 If non-NULL, storage for the 2nd reply argument.
471 * @param r3 If non-NULL, storage for the 3rd reply argument.
472 * @param r4 If non-NULL, storage for the 4th reply argument.
473 * @param r5 If non-NULL, storage for the 5th reply argument.
474 *
475 * @return Return code of the reply or an error code.
476 *
477 */
478errno_t async_req_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
479 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
480 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
481{
482 if (exch == NULL)
483 return ENOENT;
484
485 ipc_call_t result;
486 aid_t aid = async_send_5(exch, imethod, arg1, arg2, arg3, arg4, arg5,
487 &result);
488
489 errno_t rc;
490 async_wait_for(aid, &rc);
491
492 if (r1)
493 *r1 = IPC_GET_ARG1(result);
494
495 if (r2)
496 *r2 = IPC_GET_ARG2(result);
497
498 if (r3)
499 *r3 = IPC_GET_ARG3(result);
500
501 if (r4)
502 *r4 = IPC_GET_ARG4(result);
503
504 if (r5)
505 *r5 = IPC_GET_ARG5(result);
506
507 return rc;
508}
509
510void async_msg_0(async_exch_t *exch, sysarg_t imethod)
511{
512 if (exch != NULL)
513 ipc_call_async_0(exch->phone, imethod, NULL);
514}
515
516void async_msg_1(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1)
517{
518 if (exch != NULL)
519 ipc_call_async_1(exch->phone, imethod, arg1, NULL);
520}
521
522void async_msg_2(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
523 sysarg_t arg2)
524{
525 if (exch != NULL)
526 ipc_call_async_2(exch->phone, imethod, arg1, arg2, NULL);
527}
528
529void async_msg_3(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
530 sysarg_t arg2, sysarg_t arg3)
531{
532 if (exch != NULL)
533 ipc_call_async_3(exch->phone, imethod, arg1, arg2, arg3, NULL);
534}
535
536void async_msg_4(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
537 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
538{
539 if (exch != NULL)
540 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4,
541 NULL);
542}
543
544void async_msg_5(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
545 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
546{
547 if (exch != NULL)
548 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4,
549 arg5, NULL);
550}
551
552static errno_t async_connect_me_to_internal(cap_phone_handle_t phone,
553 iface_t iface, sysarg_t arg2, sysarg_t arg3, sysarg_t flags,
554 cap_phone_handle_t *out_phone)
555{
556 ipc_call_t result;
557
558 // XXX: Workaround for GCC's inability to infer association between
559 // rc == EOK and *out_phone being assigned.
560 *out_phone = CAP_NIL;
561
562 amsg_t *msg = amsg_create();
563 if (!msg)
564 return ENOENT;
565
566 msg->dataptr = &result;
567
568 errno_t rc = ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO,
569 (sysarg_t) iface, arg2, arg3, flags, msg);
570 if (rc != EOK) {
571 msg->retval = rc;
572 msg->done = true;
573 }
574
575 async_wait_for((aid_t) msg, &rc);
576
577 if (rc != EOK)
578 return rc;
579
580 *out_phone = (cap_phone_handle_t) IPC_GET_ARG5(result);
581 return EOK;
582}
583
584/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
585 *
586 * Ask through phone for a new connection to some service and block until
587 * success.
588 *
589 * @param exch Exchange for sending the message.
590 * @param iface Connection interface.
591 * @param arg2 User defined argument.
592 * @param arg3 User defined argument.
593 *
594 * @return New session on success or NULL on error.
595 *
596 */
597async_sess_t *async_connect_me_to(async_exch_t *exch, iface_t iface,
598 sysarg_t arg2, sysarg_t arg3)
599{
600 if (exch == NULL) {
601 errno = ENOENT;
602 return NULL;
603 }
604
605 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
606 if (sess == NULL) {
607 errno = ENOMEM;
608 return NULL;
609 }
610
611 cap_phone_handle_t phone;
612 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
613 arg3, 0, &phone);
614 if (rc != EOK) {
615 errno = rc;
616 free(sess);
617 return NULL;
618 }
619
620 sess->iface = iface;
621 sess->phone = phone;
622 sess->arg1 = iface;
623 sess->arg2 = arg2;
624 sess->arg3 = arg3;
625
626 fibril_mutex_initialize(&sess->remote_state_mtx);
627 sess->remote_state_data = NULL;
628
629 list_initialize(&sess->exch_list);
630 fibril_mutex_initialize(&sess->mutex);
631 atomic_set(&sess->refcnt, 0);
632
633 return sess;
634}
635
636/** Set arguments for new connections.
637 *
638 * FIXME This is an ugly hack to work around the problem that parallel
639 * exchanges are implemented using parallel connections. When we create
640 * a callback session, the framework does not know arguments for the new
641 * connections.
642 *
643 * The proper solution seems to be to implement parallel exchanges using
644 * tagging.
645 *
646 */
647void async_sess_args_set(async_sess_t *sess, iface_t iface, sysarg_t arg2,
648 sysarg_t arg3)
649{
650 sess->arg1 = iface;
651 sess->arg2 = arg2;
652 sess->arg3 = arg3;
653}
654
655/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
656 *
657 * Ask through phone for a new connection to some service and block until
658 * success.
659 *
660 * @param exch Exchange for sending the message.
661 * @param iface Connection interface.
662 * @param arg2 User defined argument.
663 * @param arg3 User defined argument.
664 *
665 * @return New session on success or NULL on error.
666 *
667 */
668async_sess_t *async_connect_me_to_blocking(async_exch_t *exch, iface_t iface,
669 sysarg_t arg2, sysarg_t arg3)
670{
671 if (exch == NULL) {
672 errno = ENOENT;
673 return NULL;
674 }
675
676 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
677 if (sess == NULL) {
678 errno = ENOMEM;
679 return NULL;
680 }
681
682 cap_phone_handle_t phone;
683 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
684 arg3, IPC_FLAG_BLOCKING, &phone);
685 if (rc != EOK) {
686 errno = rc;
687 free(sess);
688 return NULL;
689 }
690
691 sess->iface = iface;
692 sess->phone = phone;
693 sess->arg1 = iface;
694 sess->arg2 = arg2;
695 sess->arg3 = arg3;
696
697 fibril_mutex_initialize(&sess->remote_state_mtx);
698 sess->remote_state_data = NULL;
699
700 list_initialize(&sess->exch_list);
701 fibril_mutex_initialize(&sess->mutex);
702 atomic_set(&sess->refcnt, 0);
703
704 return sess;
705}
706
707/** Connect to a task specified by id.
708 *
709 */
710async_sess_t *async_connect_kbox(task_id_t id)
711{
712 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
713 if (sess == NULL) {
714 errno = ENOMEM;
715 return NULL;
716 }
717
718 cap_phone_handle_t phone;
719 errno_t rc = ipc_connect_kbox(id, &phone);
720 if (rc != EOK) {
721 errno = rc;
722 free(sess);
723 return NULL;
724 }
725
726 sess->iface = 0;
727 sess->mgmt = EXCHANGE_ATOMIC;
728 sess->phone = phone;
729 sess->arg1 = 0;
730 sess->arg2 = 0;
731 sess->arg3 = 0;
732
733 fibril_mutex_initialize(&sess->remote_state_mtx);
734 sess->remote_state_data = NULL;
735
736 list_initialize(&sess->exch_list);
737 fibril_mutex_initialize(&sess->mutex);
738 atomic_set(&sess->refcnt, 0);
739
740 return sess;
741}
742
743static errno_t async_hangup_internal(cap_phone_handle_t phone)
744{
745 return ipc_hangup(phone);
746}
747
748/** Wrapper for ipc_hangup.
749 *
750 * @param sess Session to hung up.
751 *
752 * @return Zero on success or an error code.
753 *
754 */
755errno_t async_hangup(async_sess_t *sess)
756{
757 async_exch_t *exch;
758
759 assert(sess);
760
761 if (atomic_get(&sess->refcnt) > 0)
762 return EBUSY;
763
764 fibril_mutex_lock(&async_sess_mutex);
765
766 errno_t rc = async_hangup_internal(sess->phone);
767
768 while (!list_empty(&sess->exch_list)) {
769 exch = (async_exch_t *)
770 list_get_instance(list_first(&sess->exch_list),
771 async_exch_t, sess_link);
772
773 list_remove(&exch->sess_link);
774 list_remove(&exch->global_link);
775 async_hangup_internal(exch->phone);
776 free(exch);
777 }
778
779 free(sess);
780
781 fibril_mutex_unlock(&async_sess_mutex);
782
783 return rc;
784}
785
786/** Start new exchange in a session.
787 *
788 * @param session Session.
789 *
790 * @return New exchange or NULL on error.
791 *
792 */
793async_exch_t *async_exchange_begin(async_sess_t *sess)
794{
795 if (sess == NULL)
796 return NULL;
797
798 exch_mgmt_t mgmt = sess->mgmt;
799 if (sess->iface != 0)
800 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
801
802 async_exch_t *exch = NULL;
803
804 fibril_mutex_lock(&async_sess_mutex);
805
806 if (!list_empty(&sess->exch_list)) {
807 /*
808 * There are inactive exchanges in the session.
809 */
810 exch = (async_exch_t *)
811 list_get_instance(list_first(&sess->exch_list),
812 async_exch_t, sess_link);
813
814 list_remove(&exch->sess_link);
815 list_remove(&exch->global_link);
816 } else {
817 /*
818 * There are no available exchanges in the session.
819 */
820
821 if ((mgmt == EXCHANGE_ATOMIC) ||
822 (mgmt == EXCHANGE_SERIALIZE)) {
823 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
824 if (exch != NULL) {
825 link_initialize(&exch->sess_link);
826 link_initialize(&exch->global_link);
827 exch->sess = sess;
828 exch->phone = sess->phone;
829 }
830 } else if (mgmt == EXCHANGE_PARALLEL) {
831 cap_phone_handle_t phone;
832 errno_t rc;
833
834 retry:
835 /*
836 * Make a one-time attempt to connect a new data phone.
837 */
838 rc = async_connect_me_to_internal(sess->phone, sess->arg1,
839 sess->arg2, sess->arg3, 0, &phone);
840 if (rc == EOK) {
841 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
842 if (exch != NULL) {
843 link_initialize(&exch->sess_link);
844 link_initialize(&exch->global_link);
845 exch->sess = sess;
846 exch->phone = phone;
847 } else
848 async_hangup_internal(phone);
849 } else if (!list_empty(&inactive_exch_list)) {
850 /*
851 * We did not manage to connect a new phone. But we
852 * can try to close some of the currently inactive
853 * connections in other sessions and try again.
854 */
855 exch = (async_exch_t *)
856 list_get_instance(list_first(&inactive_exch_list),
857 async_exch_t, global_link);
858
859 list_remove(&exch->sess_link);
860 list_remove(&exch->global_link);
861 async_hangup_internal(exch->phone);
862 free(exch);
863 goto retry;
864 } else {
865 /*
866 * Wait for a phone to become available.
867 */
868 fibril_condvar_wait(&avail_phone_cv, &async_sess_mutex);
869 goto retry;
870 }
871 }
872 }
873
874 fibril_mutex_unlock(&async_sess_mutex);
875
876 if (exch != NULL) {
877 atomic_inc(&sess->refcnt);
878
879 if (mgmt == EXCHANGE_SERIALIZE)
880 fibril_mutex_lock(&sess->mutex);
881 }
882
883 return exch;
884}
885
886/** Finish an exchange.
887 *
888 * @param exch Exchange to finish.
889 *
890 */
891void async_exchange_end(async_exch_t *exch)
892{
893 if (exch == NULL)
894 return;
895
896 async_sess_t *sess = exch->sess;
897 assert(sess != NULL);
898
899 exch_mgmt_t mgmt = sess->mgmt;
900 if (sess->iface != 0)
901 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
902
903 atomic_dec(&sess->refcnt);
904
905 if (mgmt == EXCHANGE_SERIALIZE)
906 fibril_mutex_unlock(&sess->mutex);
907
908 fibril_mutex_lock(&async_sess_mutex);
909
910 list_append(&exch->sess_link, &sess->exch_list);
911 list_append(&exch->global_link, &inactive_exch_list);
912 fibril_condvar_signal(&avail_phone_cv);
913
914 fibril_mutex_unlock(&async_sess_mutex);
915}
916
917/** Wrapper for IPC_M_SHARE_IN calls using the async framework.
918 *
919 * @param exch Exchange for sending the message.
920 * @param size Size of the destination address space area.
921 * @param arg User defined argument.
922 * @param flags Storage for the received flags. Can be NULL.
923 * @param dst Address of the storage for the destination address space area
924 * base address. Cannot be NULL.
925 *
926 * @return Zero on success or an error code from errno.h.
927 *
928 */
929errno_t async_share_in_start(async_exch_t *exch, size_t size, sysarg_t arg,
930 unsigned int *flags, void **dst)
931{
932 if (exch == NULL)
933 return ENOENT;
934
935 sysarg_t _flags = 0;
936 sysarg_t _dst = (sysarg_t) -1;
937 errno_t res = async_req_2_4(exch, IPC_M_SHARE_IN, (sysarg_t) size,
938 arg, NULL, &_flags, NULL, &_dst);
939
940 if (flags)
941 *flags = (unsigned int) _flags;
942
943 *dst = (void *) _dst;
944 return res;
945}
946
947/** Wrapper for IPC_M_SHARE_OUT calls using the async framework.
948 *
949 * @param exch Exchange for sending the message.
950 * @param src Source address space area base address.
951 * @param flags Flags to be used for sharing. Bits can be only cleared.
952 *
953 * @return Zero on success or an error code from errno.h.
954 *
955 */
956errno_t async_share_out_start(async_exch_t *exch, void *src, unsigned int flags)
957{
958 if (exch == NULL)
959 return ENOENT;
960
961 return async_req_3_0(exch, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
962 (sysarg_t) flags);
963}
964
965/** Start IPC_M_DATA_READ using the async framework.
966 *
967 * @param exch Exchange for sending the message.
968 * @param dst Address of the beginning of the destination buffer.
969 * @param size Size of the destination buffer (in bytes).
970 * @param dataptr Storage of call data (arg 2 holds actual data size).
971 *
972 * @return Hash of the sent message or 0 on error.
973 *
974 */
975aid_t async_data_read(async_exch_t *exch, void *dst, size_t size,
976 ipc_call_t *dataptr)
977{
978 return async_send_2(exch, IPC_M_DATA_READ, (sysarg_t) dst,
979 (sysarg_t) size, dataptr);
980}
981
982/** Wrapper for IPC_M_DATA_READ calls using the async framework.
983 *
984 * @param exch Exchange for sending the message.
985 * @param dst Address of the beginning of the destination buffer.
986 * @param size Size of the destination buffer.
987 *
988 * @return Zero on success or an error code from errno.h.
989 *
990 */
991errno_t async_data_read_start(async_exch_t *exch, void *dst, size_t size)
992{
993 if (exch == NULL)
994 return ENOENT;
995
996 return async_req_2_0(exch, IPC_M_DATA_READ, (sysarg_t) dst,
997 (sysarg_t) size);
998}
999
1000/** Wrapper for IPC_M_DATA_WRITE calls using the async framework.
1001 *
1002 * @param exch Exchange for sending the message.
1003 * @param src Address of the beginning of the source buffer.
1004 * @param size Size of the source buffer.
1005 *
1006 * @return Zero on success or an error code from errno.h.
1007 *
1008 */
1009errno_t async_data_write_start(async_exch_t *exch, const void *src, size_t size)
1010{
1011 if (exch == NULL)
1012 return ENOENT;
1013
1014 return async_req_2_0(exch, IPC_M_DATA_WRITE, (sysarg_t) src,
1015 (sysarg_t) size);
1016}
1017
1018errno_t async_state_change_start(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
1019 sysarg_t arg3, async_exch_t *other_exch)
1020{
1021 return async_req_5_0(exch, IPC_M_STATE_CHANGE_AUTHORIZE,
1022 arg1, arg2, arg3, 0, CAP_HANDLE_RAW(other_exch->phone));
1023}
1024
1025/** Lock and get session remote state
1026 *
1027 * Lock and get the local replica of the remote state
1028 * in stateful sessions. The call should be paired
1029 * with async_remote_state_release*().
1030 *
1031 * @param[in] sess Stateful session.
1032 *
1033 * @return Local replica of the remote state.
1034 *
1035 */
1036void *async_remote_state_acquire(async_sess_t *sess)
1037{
1038 fibril_mutex_lock(&sess->remote_state_mtx);
1039 return sess->remote_state_data;
1040}
1041
1042/** Update the session remote state
1043 *
1044 * Update the local replica of the remote state
1045 * in stateful sessions. The remote state must
1046 * be already locked.
1047 *
1048 * @param[in] sess Stateful session.
1049 * @param[in] state New local replica of the remote state.
1050 *
1051 */
1052void async_remote_state_update(async_sess_t *sess, void *state)
1053{
1054 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
1055 sess->remote_state_data = state;
1056}
1057
1058/** Release the session remote state
1059 *
1060 * Unlock the local replica of the remote state
1061 * in stateful sessions.
1062 *
1063 * @param[in] sess Stateful session.
1064 *
1065 */
1066void async_remote_state_release(async_sess_t *sess)
1067{
1068 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
1069
1070 fibril_mutex_unlock(&sess->remote_state_mtx);
1071}
1072
1073/** Release the session remote state and end an exchange
1074 *
1075 * Unlock the local replica of the remote state
1076 * in stateful sessions. This is convenience function
1077 * which gets the session pointer from the exchange
1078 * and also ends the exchange.
1079 *
1080 * @param[in] exch Stateful session's exchange.
1081 *
1082 */
1083void async_remote_state_release_exchange(async_exch_t *exch)
1084{
1085 if (exch == NULL)
1086 return;
1087
1088 async_sess_t *sess = exch->sess;
1089 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
1090
1091 async_exchange_end(exch);
1092 fibril_mutex_unlock(&sess->remote_state_mtx);
1093}
1094
1095void *async_as_area_create(void *base, size_t size, unsigned int flags,
1096 async_sess_t *pager, sysarg_t id1, sysarg_t id2, sysarg_t id3)
1097{
1098 as_area_pager_info_t pager_info = {
1099 .pager = pager->phone,
1100 .id1 = id1,
1101 .id2 = id2,
1102 .id3 = id3
1103 };
1104 return as_area_create(base, size, flags, &pager_info);
1105}
1106
1107/** @}
1108 */
Note: See TracBrowser for help on using the repository browser.