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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8080262 was 8080262, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Replace remaining explicit uses of futex_t outside fibril implementation, and get rid of async_futex.

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