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

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

Preallocate waitq handle during initialization

Do not clutter futex_down_composable() with the preallocation of the
wait queue handle and do it single-threadedly in futex_initialize().

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