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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 25f6bddb was 25f6bddb, checked in by Jakub Jermar <jakub@…>, 7 years ago

Deallocate waitq's used by the loader

  • 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 <time.h>
113#include <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_t 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 if (fibril_rmutex_initialize(&message_mutex) != EOK)
176 abort();
177
178 session_ns.iface = 0;
179 session_ns.mgmt = EXCHANGE_ATOMIC;
180 session_ns.phone = PHONE_NS;
181 session_ns.arg1 = 0;
182 session_ns.arg2 = 0;
183 session_ns.arg3 = 0;
184
185 fibril_mutex_initialize(&session_ns.remote_state_mtx);
186 session_ns.remote_state_data = NULL;
187
188 list_initialize(&session_ns.exch_list);
189 fibril_mutex_initialize(&session_ns.mutex);
190 session_ns.exchanges = 0;
191}
192
193void __async_client_fini(void)
194{
195 fibril_rmutex_destroy(&message_mutex);
196}
197
198/** Reply received callback.
199 *
200 * This function is called whenever a reply for an asynchronous message sent out
201 * by the asynchronous framework is received.
202 *
203 * Notify the fibril which is waiting for this message that it has arrived.
204 *
205 * @param arg Pointer to the asynchronous message record.
206 * @param retval Value returned in the answer.
207 * @param data Call data of the answer.
208 *
209 */
210void async_reply_received(ipc_call_t *data)
211{
212 amsg_t *msg = (amsg_t *) data->answer_label;
213 if (!msg)
214 return;
215
216 fibril_rmutex_lock(&message_mutex);
217
218 msg->retval = IPC_GET_RETVAL(*data);
219
220 /* Copy data inside lock, just in case the call was detached */
221 if ((msg->dataptr) && (data))
222 *msg->dataptr = *data;
223
224 msg->done = true;
225
226 if (msg->forget) {
227 amsg_destroy(msg);
228 } else {
229 fibril_notify(&msg->received);
230 }
231
232 fibril_rmutex_unlock(&message_mutex);
233}
234
235/** Send message and return id of the sent message.
236 *
237 * The return value can be used as input for async_wait() to wait for
238 * completion.
239 *
240 * @param exch Exchange for sending the message.
241 * @param imethod Service-defined interface and method.
242 * @param arg1 Service-defined payload argument.
243 * @param arg2 Service-defined payload argument.
244 * @param arg3 Service-defined payload argument.
245 * @param arg4 Service-defined payload argument.
246 * @param dataptr If non-NULL, storage where the reply data will be stored.
247 *
248 * @return Hash of the sent message or 0 on error.
249 *
250 */
251aid_t async_send_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
252 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
253{
254 if (exch == NULL)
255 return 0;
256
257 amsg_t *msg = amsg_create();
258 if (msg == NULL)
259 return 0;
260
261 msg->dataptr = dataptr;
262
263 errno_t rc = ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3,
264 arg4, msg);
265 if (rc != EOK) {
266 msg->retval = rc;
267 msg->done = true;
268 }
269
270 return (aid_t) msg;
271}
272
273/** Send message and return id of the sent message
274 *
275 * The return value can be used as input for async_wait() to wait for
276 * completion.
277 *
278 * @param exch Exchange for sending the message.
279 * @param imethod Service-defined interface and method.
280 * @param arg1 Service-defined payload argument.
281 * @param arg2 Service-defined payload argument.
282 * @param arg3 Service-defined payload argument.
283 * @param arg4 Service-defined payload argument.
284 * @param arg5 Service-defined payload argument.
285 * @param dataptr If non-NULL, storage where the reply data will be
286 * stored.
287 *
288 * @return Hash of the sent message or 0 on error.
289 *
290 */
291aid_t async_send_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
292 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
293 ipc_call_t *dataptr)
294{
295 if (exch == NULL)
296 return 0;
297
298 amsg_t *msg = amsg_create();
299 if (msg == NULL)
300 return 0;
301
302 msg->dataptr = dataptr;
303
304 errno_t rc = ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3,
305 arg4, arg5, msg);
306 if (rc != EOK) {
307 msg->retval = rc;
308 msg->done = true;
309 }
310
311 return (aid_t) msg;
312}
313
314/** Wait for a message sent by the async framework.
315 *
316 * @param amsgid Hash of the message to wait for.
317 * @param retval Pointer to storage where the retval of the answer will
318 * be stored.
319 *
320 */
321void async_wait_for(aid_t amsgid, errno_t *retval)
322{
323 if (amsgid == 0) {
324 if (retval)
325 *retval = ENOMEM;
326 return;
327 }
328
329 amsg_t *msg = (amsg_t *) amsgid;
330 fibril_wait_for(&msg->received);
331
332 if (retval)
333 *retval = msg->retval;
334
335 amsg_destroy(msg);
336}
337
338/** Wait for a message sent by the async framework, timeout variant.
339 *
340 * If the wait times out, the caller may choose to either wait again by calling
341 * async_wait_for() or async_wait_timeout(), or forget the message via
342 * async_forget().
343 *
344 * @param amsgid Hash of the message to wait for.
345 * @param retval Pointer to storage where the retval of the answer will
346 * be stored.
347 * @param timeout Timeout in microseconds.
348 *
349 * @return Zero on success, ETIMEOUT if the timeout has expired.
350 *
351 */
352errno_t async_wait_timeout(aid_t amsgid, errno_t *retval, usec_t timeout)
353{
354 if (amsgid == 0) {
355 if (retval)
356 *retval = ENOMEM;
357 return EOK;
358 }
359
360 amsg_t *msg = (amsg_t *) amsgid;
361
362 /*
363 * Negative timeout is converted to zero timeout to avoid
364 * using tv_add with negative augmenter.
365 */
366 if (timeout < 0)
367 timeout = 0;
368
369 struct timespec expires;
370 getuptime(&expires);
371 ts_add_diff(&expires, USEC2NSEC(timeout));
372
373 errno_t rc = fibril_wait_timeout(&msg->received, &expires);
374 if (rc != EOK)
375 return rc;
376
377 if (retval)
378 *retval = msg->retval;
379
380 amsg_destroy(msg);
381
382 return EOK;
383}
384
385/** Discard the message / reply on arrival.
386 *
387 * The message will be marked to be discarded once the reply arrives in
388 * reply_received(). It is not allowed to call async_wait_for() or
389 * async_wait_timeout() on this message after a call to this function.
390 *
391 * @param amsgid Hash of the message to forget.
392 */
393void async_forget(aid_t amsgid)
394{
395 if (amsgid == 0)
396 return;
397
398 amsg_t *msg = (amsg_t *) amsgid;
399
400 assert(!msg->forget);
401
402 fibril_rmutex_lock(&message_mutex);
403
404 if (msg->done) {
405 amsg_destroy(msg);
406 } else {
407 msg->dataptr = NULL;
408 msg->forget = true;
409 }
410
411 fibril_rmutex_unlock(&message_mutex);
412}
413
414/** Pseudo-synchronous message sending - fast version.
415 *
416 * Send message asynchronously and return only after the reply arrives.
417 *
418 * This function can only transfer 4 register payload arguments. For
419 * transferring more arguments, see the slower async_req_slow().
420 *
421 * @param exch Exchange for sending the message.
422 * @param imethod Interface and method of the call.
423 * @param arg1 Service-defined payload argument.
424 * @param arg2 Service-defined payload argument.
425 * @param arg3 Service-defined payload argument.
426 * @param arg4 Service-defined payload argument.
427 * @param r1 If non-NULL, storage for the 1st reply argument.
428 * @param r2 If non-NULL, storage for the 2nd reply argument.
429 * @param r3 If non-NULL, storage for the 3rd reply argument.
430 * @param r4 If non-NULL, storage for the 4th reply argument.
431 * @param r5 If non-NULL, storage for the 5th reply argument.
432 *
433 * @return Return code of the reply or an error code.
434 *
435 */
436errno_t async_req_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
437 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
438 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
439{
440 if (exch == NULL)
441 return ENOENT;
442
443 ipc_call_t result;
444 aid_t aid = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
445 &result);
446
447 errno_t rc;
448 async_wait_for(aid, &rc);
449
450 if (r1)
451 *r1 = IPC_GET_ARG1(result);
452
453 if (r2)
454 *r2 = IPC_GET_ARG2(result);
455
456 if (r3)
457 *r3 = IPC_GET_ARG3(result);
458
459 if (r4)
460 *r4 = IPC_GET_ARG4(result);
461
462 if (r5)
463 *r5 = IPC_GET_ARG5(result);
464
465 return rc;
466}
467
468/** Pseudo-synchronous message sending - slow version.
469 *
470 * Send message asynchronously and return only after the reply arrives.
471 *
472 * @param exch Exchange for sending the message.
473 * @param imethod Interface and method of the call.
474 * @param arg1 Service-defined payload argument.
475 * @param arg2 Service-defined payload argument.
476 * @param arg3 Service-defined payload argument.
477 * @param arg4 Service-defined payload argument.
478 * @param arg5 Service-defined payload argument.
479 * @param r1 If non-NULL, storage for the 1st reply argument.
480 * @param r2 If non-NULL, storage for the 2nd reply argument.
481 * @param r3 If non-NULL, storage for the 3rd reply argument.
482 * @param r4 If non-NULL, storage for the 4th reply argument.
483 * @param r5 If non-NULL, storage for the 5th reply argument.
484 *
485 * @return Return code of the reply or an error code.
486 *
487 */
488errno_t async_req_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
489 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
490 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
491{
492 if (exch == NULL)
493 return ENOENT;
494
495 ipc_call_t result;
496 aid_t aid = async_send_5(exch, imethod, arg1, arg2, arg3, arg4, arg5,
497 &result);
498
499 errno_t rc;
500 async_wait_for(aid, &rc);
501
502 if (r1)
503 *r1 = IPC_GET_ARG1(result);
504
505 if (r2)
506 *r2 = IPC_GET_ARG2(result);
507
508 if (r3)
509 *r3 = IPC_GET_ARG3(result);
510
511 if (r4)
512 *r4 = IPC_GET_ARG4(result);
513
514 if (r5)
515 *r5 = IPC_GET_ARG5(result);
516
517 return rc;
518}
519
520void async_msg_0(async_exch_t *exch, sysarg_t imethod)
521{
522 if (exch != NULL)
523 ipc_call_async_0(exch->phone, imethod, NULL);
524}
525
526void async_msg_1(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1)
527{
528 if (exch != NULL)
529 ipc_call_async_1(exch->phone, imethod, arg1, NULL);
530}
531
532void async_msg_2(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
533 sysarg_t arg2)
534{
535 if (exch != NULL)
536 ipc_call_async_2(exch->phone, imethod, arg1, arg2, NULL);
537}
538
539void async_msg_3(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
540 sysarg_t arg2, sysarg_t arg3)
541{
542 if (exch != NULL)
543 ipc_call_async_3(exch->phone, imethod, arg1, arg2, arg3, NULL);
544}
545
546void async_msg_4(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
547 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
548{
549 if (exch != NULL)
550 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4,
551 NULL);
552}
553
554void async_msg_5(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
555 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
556{
557 if (exch != NULL)
558 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4,
559 arg5, NULL);
560}
561
562static errno_t async_connect_me_to_internal(cap_phone_handle_t phone,
563 iface_t iface, sysarg_t arg2, sysarg_t arg3, sysarg_t flags,
564 cap_phone_handle_t *out_phone)
565{
566 ipc_call_t result;
567
568 // XXX: Workaround for GCC's inability to infer association between
569 // rc == EOK and *out_phone being assigned.
570 *out_phone = CAP_NIL;
571
572 amsg_t *msg = amsg_create();
573 if (!msg)
574 return ENOENT;
575
576 msg->dataptr = &result;
577
578 errno_t rc = ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO,
579 (sysarg_t) iface, arg2, arg3, flags, msg);
580 if (rc != EOK) {
581 msg->retval = rc;
582 msg->done = true;
583 }
584
585 async_wait_for((aid_t) msg, &rc);
586
587 if (rc != EOK)
588 return rc;
589
590 *out_phone = (cap_phone_handle_t) IPC_GET_ARG5(result);
591 return EOK;
592}
593
594/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
595 *
596 * Ask through phone for a new connection to some service and block until
597 * success.
598 *
599 * @param exch Exchange for sending the message.
600 * @param iface Connection interface.
601 * @param arg2 User defined argument.
602 * @param arg3 User defined argument.
603 *
604 * @return New session on success or NULL on error.
605 *
606 */
607async_sess_t *async_connect_me_to(async_exch_t *exch, iface_t iface,
608 sysarg_t arg2, sysarg_t arg3)
609{
610 if (exch == NULL) {
611 errno = ENOENT;
612 return NULL;
613 }
614
615 async_sess_t *sess = calloc(1, sizeof(async_sess_t));
616 if (sess == NULL) {
617 errno = ENOMEM;
618 return NULL;
619 }
620
621 cap_phone_handle_t phone;
622 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
623 arg3, 0, &phone);
624 if (rc != EOK) {
625 errno = rc;
626 free(sess);
627 return NULL;
628 }
629
630 sess->iface = iface;
631 sess->phone = phone;
632 sess->arg1 = iface;
633 sess->arg2 = arg2;
634 sess->arg3 = arg3;
635
636 fibril_mutex_initialize(&sess->remote_state_mtx);
637 list_initialize(&sess->exch_list);
638 fibril_mutex_initialize(&sess->mutex);
639
640 return sess;
641}
642
643/** Set arguments for new connections.
644 *
645 * FIXME This is an ugly hack to work around the problem that parallel
646 * exchanges are implemented using parallel connections. When we create
647 * a callback session, the framework does not know arguments for the new
648 * connections.
649 *
650 * The proper solution seems to be to implement parallel exchanges using
651 * tagging.
652 *
653 */
654void async_sess_args_set(async_sess_t *sess, iface_t iface, sysarg_t arg2,
655 sysarg_t arg3)
656{
657 sess->arg1 = iface;
658 sess->arg2 = arg2;
659 sess->arg3 = arg3;
660}
661
662/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
663 *
664 * Ask through phone for a new connection to some service and block until
665 * success.
666 *
667 * @param exch Exchange for sending the message.
668 * @param iface Connection interface.
669 * @param arg2 User defined argument.
670 * @param arg3 User defined argument.
671 *
672 * @return New session on success or NULL on error.
673 *
674 */
675async_sess_t *async_connect_me_to_blocking(async_exch_t *exch, iface_t iface,
676 sysarg_t arg2, sysarg_t arg3)
677{
678 if (exch == NULL) {
679 errno = ENOENT;
680 return NULL;
681 }
682
683 async_sess_t *sess = calloc(1, sizeof(async_sess_t));
684 if (sess == NULL) {
685 errno = ENOMEM;
686 return NULL;
687 }
688
689 cap_phone_handle_t phone;
690 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
691 arg3, IPC_FLAG_BLOCKING, &phone);
692 if (rc != EOK) {
693 errno = rc;
694 free(sess);
695 return NULL;
696 }
697
698 sess->iface = iface;
699 sess->phone = phone;
700 sess->arg1 = iface;
701 sess->arg2 = arg2;
702 sess->arg3 = arg3;
703
704 fibril_mutex_initialize(&sess->remote_state_mtx);
705 list_initialize(&sess->exch_list);
706 fibril_mutex_initialize(&sess->mutex);
707
708 return sess;
709}
710
711/** Connect to a task specified by id.
712 *
713 */
714async_sess_t *async_connect_kbox(task_id_t id)
715{
716 async_sess_t *sess = calloc(1, sizeof(async_sess_t));
717 if (sess == NULL) {
718 errno = ENOMEM;
719 return NULL;
720 }
721
722 cap_phone_handle_t phone;
723 errno_t rc = ipc_connect_kbox(id, &phone);
724 if (rc != EOK) {
725 errno = rc;
726 free(sess);
727 return NULL;
728 }
729
730 sess->iface = 0;
731 sess->mgmt = EXCHANGE_ATOMIC;
732 sess->phone = phone;
733
734 fibril_mutex_initialize(&sess->remote_state_mtx);
735 list_initialize(&sess->exch_list);
736 fibril_mutex_initialize(&sess->mutex);
737
738 return sess;
739}
740
741static errno_t async_hangup_internal(cap_phone_handle_t phone)
742{
743 return ipc_hangup(phone);
744}
745
746/** Wrapper for ipc_hangup.
747 *
748 * @param sess Session to hung up.
749 *
750 * @return Zero on success or an error code.
751 *
752 */
753errno_t async_hangup(async_sess_t *sess)
754{
755 async_exch_t *exch;
756
757 assert(sess);
758
759 fibril_mutex_lock(&async_sess_mutex);
760
761 if (sess->exchanges > 0) {
762 fibril_mutex_unlock(&async_sess_mutex);
763 return EBUSY;
764 }
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 if (exch != NULL)
875 sess->exchanges++;
876
877 fibril_mutex_unlock(&async_sess_mutex);
878
879 if (exch != NULL && mgmt == EXCHANGE_SERIALIZE)
880 fibril_mutex_lock(&sess->mutex);
881
882 return exch;
883}
884
885/** Finish an exchange.
886 *
887 * @param exch Exchange to finish.
888 *
889 */
890void async_exchange_end(async_exch_t *exch)
891{
892 if (exch == NULL)
893 return;
894
895 async_sess_t *sess = exch->sess;
896 assert(sess != NULL);
897
898 exch_mgmt_t mgmt = sess->mgmt;
899 if (sess->iface != 0)
900 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
901
902 if (mgmt == EXCHANGE_SERIALIZE)
903 fibril_mutex_unlock(&sess->mutex);
904
905 fibril_mutex_lock(&async_sess_mutex);
906
907 sess->exchanges--;
908
909 list_append(&exch->sess_link, &sess->exch_list);
910 list_append(&exch->global_link, &inactive_exch_list);
911 fibril_condvar_signal(&avail_phone_cv);
912
913 fibril_mutex_unlock(&async_sess_mutex);
914}
915
916/** Wrapper for IPC_M_SHARE_IN calls using the async framework.
917 *
918 * @param exch Exchange for sending the message.
919 * @param size Size of the destination address space area.
920 * @param arg User defined argument.
921 * @param flags Storage for the received flags. Can be NULL.
922 * @param dst Address of the storage for the destination address space area
923 * base address. Cannot be NULL.
924 *
925 * @return Zero on success or an error code from errno.h.
926 *
927 */
928errno_t async_share_in_start(async_exch_t *exch, size_t size, sysarg_t arg,
929 unsigned int *flags, void **dst)
930{
931 if (exch == NULL)
932 return ENOENT;
933
934 sysarg_t _flags = 0;
935 sysarg_t _dst = (sysarg_t) -1;
936 errno_t res = async_req_3_5(exch, IPC_M_SHARE_IN, (sysarg_t) size,
937 (sysarg_t) __progsymbols.end, arg, NULL, &_flags, NULL, NULL,
938 &_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.