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

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

Statically allocate session_ns.

  • Property mode set to 100644
File size: 32.3 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 <futex.h>
107#include <fibril.h>
108#include <adt/hash_table.h>
109#include <adt/hash.h>
110#include <adt/list.h>
111#include <assert.h>
112#include <errno.h>
113#include <sys/time.h>
114#include <libarch/barrier.h>
115#include <stdbool.h>
116#include <stdlib.h>
117#include <mem.h>
118#include <stdlib.h>
119#include <macros.h>
120#include <as.h>
121#include <abi/mm/as.h>
122#include "../private/libc.h"
123
124/** Naming service session */
125async_sess_t session_ns;
126
127/** Message data */
128typedef struct {
129 awaiter_t wdata;
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 /** If already destroyed. */
138 bool destroyed;
139
140 /** Pointer to where the answer data is stored. */
141 ipc_call_t *dataptr;
142
143 errno_t retval;
144} amsg_t;
145
146static void to_event_initialize(to_event_t *to)
147{
148 struct timeval tv = { 0, 0 };
149
150 to->inlist = false;
151 to->occurred = false;
152 link_initialize(&to->link);
153 to->expires = tv;
154}
155
156static void wu_event_initialize(wu_event_t *wu)
157{
158 wu->inlist = false;
159 link_initialize(&wu->link);
160}
161
162void awaiter_initialize(awaiter_t *aw)
163{
164 aw->fid = 0;
165 aw->active = false;
166 to_event_initialize(&aw->to_event);
167 wu_event_initialize(&aw->wu_event);
168}
169
170static amsg_t *amsg_create(void)
171{
172 amsg_t *msg = malloc(sizeof(amsg_t));
173 if (msg) {
174 msg->done = false;
175 msg->forget = false;
176 msg->destroyed = false;
177 msg->dataptr = NULL;
178 msg->retval = EINVAL;
179 awaiter_initialize(&msg->wdata);
180 }
181
182 return msg;
183}
184
185static void amsg_destroy(amsg_t *msg)
186{
187 assert(!msg->destroyed);
188 msg->destroyed = true;
189 free(msg);
190}
191
192
193/** Mutex protecting inactive_exch_list and avail_phone_cv.
194 *
195 */
196static FIBRIL_MUTEX_INITIALIZE(async_sess_mutex);
197
198/** List of all currently inactive exchanges.
199 *
200 */
201static LIST_INITIALIZE(inactive_exch_list);
202
203/** Condition variable to wait for a phone to become available.
204 *
205 */
206static FIBRIL_CONDVAR_INITIALIZE(avail_phone_cv);
207
208/** Initialize the async framework.
209 *
210 */
211void __async_client_init(void)
212{
213 session_ns.iface = 0;
214 session_ns.mgmt = EXCHANGE_ATOMIC;
215 session_ns.phone = PHONE_NS;
216 session_ns.arg1 = 0;
217 session_ns.arg2 = 0;
218 session_ns.arg3 = 0;
219
220 fibril_mutex_initialize(&session_ns.remote_state_mtx);
221 session_ns.remote_state_data = NULL;
222
223 list_initialize(&session_ns.exch_list);
224 fibril_mutex_initialize(&session_ns.mutex);
225 atomic_set(&session_ns.refcnt, 0);
226}
227
228/** Reply received callback.
229 *
230 * This function is called whenever a reply for an asynchronous message sent out
231 * by the asynchronous framework is received.
232 *
233 * Notify the fibril which is waiting for this message that it has arrived.
234 *
235 * @param arg Pointer to the asynchronous message record.
236 * @param retval Value returned in the answer.
237 * @param data Call data of the answer.
238 *
239 */
240static void reply_received(void *arg, errno_t retval, ipc_call_t *data)
241{
242 assert(arg);
243
244 futex_down(&async_futex);
245
246 amsg_t *msg = (amsg_t *) arg;
247 msg->retval = retval;
248
249 /* Copy data after futex_down, just in case the call was detached */
250 if ((msg->dataptr) && (data))
251 *msg->dataptr = *data;
252
253 write_barrier();
254
255 /* Remove message from timeout list */
256 if (msg->wdata.to_event.inlist)
257 list_remove(&msg->wdata.to_event.link);
258
259 msg->done = true;
260
261 if (msg->forget) {
262 assert(msg->wdata.active);
263 amsg_destroy(msg);
264 } else if (!msg->wdata.active) {
265 msg->wdata.active = true;
266 fibril_add_ready(msg->wdata.fid);
267 }
268
269 futex_up(&async_futex);
270}
271
272/** Send message and return id of the sent message.
273 *
274 * The return value can be used as input for async_wait() to wait for
275 * completion.
276 *
277 * @param exch Exchange for sending the message.
278 * @param imethod Service-defined interface and method.
279 * @param arg1 Service-defined payload argument.
280 * @param arg2 Service-defined payload argument.
281 * @param arg3 Service-defined payload argument.
282 * @param arg4 Service-defined payload argument.
283 * @param dataptr If non-NULL, storage where the reply data will be stored.
284 *
285 * @return Hash of the sent message or 0 on error.
286 *
287 */
288aid_t async_send_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
289 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
290{
291 if (exch == NULL)
292 return 0;
293
294 amsg_t *msg = amsg_create();
295 if (msg == NULL)
296 return 0;
297
298 msg->dataptr = dataptr;
299 msg->wdata.active = true;
300
301 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4, msg,
302 reply_received);
303
304 return (aid_t) msg;
305}
306
307/** Send message and return id of the sent message
308 *
309 * The return value can be used as input for async_wait() to wait for
310 * completion.
311 *
312 * @param exch Exchange for sending the message.
313 * @param imethod Service-defined interface and method.
314 * @param arg1 Service-defined payload argument.
315 * @param arg2 Service-defined payload argument.
316 * @param arg3 Service-defined payload argument.
317 * @param arg4 Service-defined payload argument.
318 * @param arg5 Service-defined payload argument.
319 * @param dataptr If non-NULL, storage where the reply data will be
320 * stored.
321 *
322 * @return Hash of the sent message or 0 on error.
323 *
324 */
325aid_t async_send_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
326 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
327 ipc_call_t *dataptr)
328{
329 if (exch == NULL)
330 return 0;
331
332 amsg_t *msg = amsg_create();
333 if (msg == NULL)
334 return 0;
335
336 msg->dataptr = dataptr;
337 msg->wdata.active = true;
338
339 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4, arg5,
340 msg, reply_received);
341
342 return (aid_t) msg;
343}
344
345/** Wait for a message sent by the async framework.
346 *
347 * @param amsgid Hash of the message to wait for.
348 * @param retval Pointer to storage where the retval of the answer will
349 * be stored.
350 *
351 */
352void async_wait_for(aid_t amsgid, errno_t *retval)
353{
354 assert(amsgid);
355
356 amsg_t *msg = (amsg_t *) amsgid;
357
358 futex_down(&async_futex);
359
360 assert(!msg->forget);
361 assert(!msg->destroyed);
362
363 if (msg->done) {
364 futex_up(&async_futex);
365 goto done;
366 }
367
368 msg->wdata.fid = fibril_get_id();
369 msg->wdata.active = false;
370 msg->wdata.to_event.inlist = false;
371
372 /* Leave the async_futex locked when entering this function */
373 fibril_switch(FIBRIL_TO_MANAGER);
374
375 /* Futex is up automatically after fibril_switch */
376
377done:
378 if (retval)
379 *retval = msg->retval;
380
381 amsg_destroy(msg);
382}
383
384/** Wait for a message sent by the async framework, timeout variant.
385 *
386 * If the wait times out, the caller may choose to either wait again by calling
387 * async_wait_for() or async_wait_timeout(), or forget the message via
388 * async_forget().
389 *
390 * @param amsgid Hash of the message to wait for.
391 * @param retval Pointer to storage where the retval of the answer will
392 * be stored.
393 * @param timeout Timeout in microseconds.
394 *
395 * @return Zero on success, ETIMEOUT if the timeout has expired.
396 *
397 */
398errno_t async_wait_timeout(aid_t amsgid, errno_t *retval, suseconds_t timeout)
399{
400 assert(amsgid);
401
402 amsg_t *msg = (amsg_t *) amsgid;
403
404 futex_down(&async_futex);
405
406 assert(!msg->forget);
407 assert(!msg->destroyed);
408
409 if (msg->done) {
410 futex_up(&async_futex);
411 goto done;
412 }
413
414 /*
415 * Negative timeout is converted to zero timeout to avoid
416 * using tv_add with negative augmenter.
417 */
418 if (timeout < 0)
419 timeout = 0;
420
421 getuptime(&msg->wdata.to_event.expires);
422 tv_add_diff(&msg->wdata.to_event.expires, timeout);
423
424 /*
425 * Current fibril is inserted as waiting regardless of the
426 * "size" of the timeout.
427 *
428 * Checking for msg->done and immediately bailing out when
429 * timeout == 0 would mean that the manager fibril would never
430 * run (consider single threaded program).
431 * Thus the IPC answer would be never retrieved from the kernel.
432 *
433 * Notice that the actual delay would be very small because we
434 * - switch to manager fibril
435 * - the manager sees expired timeout
436 * - and thus adds us back to ready queue
437 * - manager switches back to some ready fibril
438 * (prior it, it checks for incoming IPC).
439 *
440 */
441 msg->wdata.fid = fibril_get_id();
442 msg->wdata.active = false;
443 async_insert_timeout(&msg->wdata);
444
445 /* Leave the async_futex locked when entering this function */
446 fibril_switch(FIBRIL_TO_MANAGER);
447
448 /* Futex is up automatically after fibril_switch */
449
450 if (!msg->done)
451 return ETIMEOUT;
452
453done:
454 if (retval)
455 *retval = msg->retval;
456
457 amsg_destroy(msg);
458
459 return 0;
460}
461
462/** Discard the message / reply on arrival.
463 *
464 * The message will be marked to be discarded once the reply arrives in
465 * reply_received(). It is not allowed to call async_wait_for() or
466 * async_wait_timeout() on this message after a call to this function.
467 *
468 * @param amsgid Hash of the message to forget.
469 */
470void async_forget(aid_t amsgid)
471{
472 amsg_t *msg = (amsg_t *) amsgid;
473
474 assert(msg);
475 assert(!msg->forget);
476 assert(!msg->destroyed);
477
478 futex_down(&async_futex);
479
480 if (msg->done) {
481 amsg_destroy(msg);
482 } else {
483 msg->dataptr = NULL;
484 msg->forget = true;
485 }
486
487 futex_up(&async_futex);
488}
489
490/** Wait for specified time.
491 *
492 * The current fibril is suspended but the thread continues to execute.
493 *
494 * @param timeout Duration of the wait in microseconds.
495 *
496 */
497void async_usleep(suseconds_t timeout)
498{
499 awaiter_t awaiter;
500 awaiter_initialize(&awaiter);
501
502 awaiter.fid = fibril_get_id();
503
504 getuptime(&awaiter.to_event.expires);
505 tv_add_diff(&awaiter.to_event.expires, timeout);
506
507 futex_down(&async_futex);
508
509 async_insert_timeout(&awaiter);
510
511 /* Leave the async_futex locked when entering this function */
512 fibril_switch(FIBRIL_TO_MANAGER);
513
514 /* Futex is up automatically after fibril_switch() */
515}
516
517/** Delay execution for the specified number of seconds
518 *
519 * @param sec Number of seconds to sleep
520 */
521void async_sleep(unsigned int sec)
522{
523 /*
524 * Sleep in 1000 second steps to support
525 * full argument range
526 */
527
528 while (sec > 0) {
529 unsigned int period = (sec > 1000) ? 1000 : sec;
530
531 async_usleep(period * 1000000);
532 sec -= period;
533 }
534}
535
536/** Pseudo-synchronous message sending - fast version.
537 *
538 * Send message asynchronously and return only after the reply arrives.
539 *
540 * This function can only transfer 4 register payload arguments. For
541 * transferring more arguments, see the slower async_req_slow().
542 *
543 * @param exch Exchange for sending the message.
544 * @param imethod Interface and method of the call.
545 * @param arg1 Service-defined payload argument.
546 * @param arg2 Service-defined payload argument.
547 * @param arg3 Service-defined payload argument.
548 * @param arg4 Service-defined payload argument.
549 * @param r1 If non-NULL, storage for the 1st reply argument.
550 * @param r2 If non-NULL, storage for the 2nd reply argument.
551 * @param r3 If non-NULL, storage for the 3rd reply argument.
552 * @param r4 If non-NULL, storage for the 4th reply argument.
553 * @param r5 If non-NULL, storage for the 5th reply argument.
554 *
555 * @return Return code of the reply or an error code.
556 *
557 */
558errno_t async_req_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
559 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
560 sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
561{
562 if (exch == NULL)
563 return ENOENT;
564
565 ipc_call_t result;
566 aid_t aid = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
567 &result);
568
569 errno_t rc;
570 async_wait_for(aid, &rc);
571
572 if (r1)
573 *r1 = IPC_GET_ARG1(result);
574
575 if (r2)
576 *r2 = IPC_GET_ARG2(result);
577
578 if (r3)
579 *r3 = IPC_GET_ARG3(result);
580
581 if (r4)
582 *r4 = IPC_GET_ARG4(result);
583
584 if (r5)
585 *r5 = IPC_GET_ARG5(result);
586
587 return rc;
588}
589
590/** Pseudo-synchronous message sending - slow version.
591 *
592 * Send message asynchronously and return only after the reply arrives.
593 *
594 * @param exch Exchange for sending the message.
595 * @param imethod Interface and method of the call.
596 * @param arg1 Service-defined payload argument.
597 * @param arg2 Service-defined payload argument.
598 * @param arg3 Service-defined payload argument.
599 * @param arg4 Service-defined payload argument.
600 * @param arg5 Service-defined payload argument.
601 * @param r1 If non-NULL, storage for the 1st reply argument.
602 * @param r2 If non-NULL, storage for the 2nd reply argument.
603 * @param r3 If non-NULL, storage for the 3rd reply argument.
604 * @param r4 If non-NULL, storage for the 4th reply argument.
605 * @param r5 If non-NULL, storage for the 5th reply argument.
606 *
607 * @return Return code of the reply or an error code.
608 *
609 */
610errno_t async_req_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
611 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
612 sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
613{
614 if (exch == NULL)
615 return ENOENT;
616
617 ipc_call_t result;
618 aid_t aid = async_send_5(exch, imethod, arg1, arg2, arg3, arg4, arg5,
619 &result);
620
621 errno_t rc;
622 async_wait_for(aid, &rc);
623
624 if (r1)
625 *r1 = IPC_GET_ARG1(result);
626
627 if (r2)
628 *r2 = IPC_GET_ARG2(result);
629
630 if (r3)
631 *r3 = IPC_GET_ARG3(result);
632
633 if (r4)
634 *r4 = IPC_GET_ARG4(result);
635
636 if (r5)
637 *r5 = IPC_GET_ARG5(result);
638
639 return rc;
640}
641
642void async_msg_0(async_exch_t *exch, sysarg_t imethod)
643{
644 if (exch != NULL)
645 ipc_call_async_0(exch->phone, imethod, NULL, NULL);
646}
647
648void async_msg_1(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1)
649{
650 if (exch != NULL)
651 ipc_call_async_1(exch->phone, imethod, arg1, NULL, NULL);
652}
653
654void async_msg_2(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
655 sysarg_t arg2)
656{
657 if (exch != NULL)
658 ipc_call_async_2(exch->phone, imethod, arg1, arg2, NULL, NULL);
659}
660
661void async_msg_3(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
662 sysarg_t arg2, sysarg_t arg3)
663{
664 if (exch != NULL)
665 ipc_call_async_3(exch->phone, imethod, arg1, arg2, arg3, NULL,
666 NULL);
667}
668
669void async_msg_4(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
670 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
671{
672 if (exch != NULL)
673 ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4,
674 NULL, NULL);
675}
676
677void async_msg_5(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
678 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
679{
680 if (exch != NULL)
681 ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4,
682 arg5, NULL, NULL);
683}
684
685static errno_t async_connect_me_to_internal(cap_phone_handle_t phone,
686 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
687 cap_phone_handle_t *out_phone)
688{
689 ipc_call_t result;
690
691 // XXX: Workaround for GCC's inability to infer association between
692 // rc == EOK and *out_phone being assigned.
693 *out_phone = CAP_NIL;
694
695 amsg_t *msg = amsg_create();
696 if (!msg)
697 return ENOENT;
698
699 msg->dataptr = &result;
700 msg->wdata.active = true;
701
702 ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, arg4,
703 msg, reply_received);
704
705 errno_t rc;
706 async_wait_for((aid_t) msg, &rc);
707
708 if (rc != EOK)
709 return rc;
710
711 *out_phone = (cap_phone_handle_t) IPC_GET_ARG5(result);
712 return EOK;
713}
714
715/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
716 *
717 * Ask through for a new connection to some service.
718 *
719 * @param mgmt Exchange management style.
720 * @param exch Exchange for sending the message.
721 * @param arg1 User defined argument.
722 * @param arg2 User defined argument.
723 * @param arg3 User defined argument.
724 *
725 * @return New session on success or NULL on error.
726 *
727 */
728async_sess_t *async_connect_me_to(exch_mgmt_t mgmt, async_exch_t *exch,
729 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
730{
731 if (exch == NULL) {
732 errno = ENOENT;
733 return NULL;
734 }
735
736 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
737 if (sess == NULL) {
738 errno = ENOMEM;
739 return NULL;
740 }
741
742 cap_phone_handle_t phone;
743 errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
744 0, &phone);
745 if (rc != EOK) {
746 errno = rc;
747 free(sess);
748 return NULL;
749 }
750
751 sess->iface = 0;
752 sess->mgmt = mgmt;
753 sess->phone = phone;
754 sess->arg1 = arg1;
755 sess->arg2 = arg2;
756 sess->arg3 = arg3;
757
758 fibril_mutex_initialize(&sess->remote_state_mtx);
759 sess->remote_state_data = NULL;
760
761 list_initialize(&sess->exch_list);
762 fibril_mutex_initialize(&sess->mutex);
763 atomic_set(&sess->refcnt, 0);
764
765 return sess;
766}
767
768/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
769 *
770 * Ask through phone for a new connection to some service and block until
771 * success.
772 *
773 * @param exch Exchange for sending the message.
774 * @param iface Connection interface.
775 * @param arg2 User defined argument.
776 * @param arg3 User defined argument.
777 *
778 * @return New session on success or NULL on error.
779 *
780 */
781async_sess_t *async_connect_me_to_iface(async_exch_t *exch, iface_t iface,
782 sysarg_t arg2, sysarg_t arg3)
783{
784 if (exch == NULL) {
785 errno = ENOENT;
786 return NULL;
787 }
788
789 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
790 if (sess == NULL) {
791 errno = ENOMEM;
792 return NULL;
793 }
794
795 cap_phone_handle_t phone;
796 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
797 arg3, 0, &phone);
798 if (rc != EOK) {
799 errno = rc;
800 free(sess);
801 return NULL;
802 }
803
804 sess->iface = iface;
805 sess->phone = phone;
806 sess->arg1 = iface;
807 sess->arg2 = arg2;
808 sess->arg3 = arg3;
809
810 fibril_mutex_initialize(&sess->remote_state_mtx);
811 sess->remote_state_data = NULL;
812
813 list_initialize(&sess->exch_list);
814 fibril_mutex_initialize(&sess->mutex);
815 atomic_set(&sess->refcnt, 0);
816
817 return sess;
818}
819
820/** Set arguments for new connections.
821 *
822 * FIXME This is an ugly hack to work around the problem that parallel
823 * exchanges are implemented using parallel connections. When we create
824 * a callback session, the framework does not know arguments for the new
825 * connections.
826 *
827 * The proper solution seems to be to implement parallel exchanges using
828 * tagging.
829 */
830void async_sess_args_set(async_sess_t *sess, sysarg_t arg1, sysarg_t arg2,
831 sysarg_t arg3)
832{
833 sess->arg1 = arg1;
834 sess->arg2 = arg2;
835 sess->arg3 = arg3;
836}
837
838/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
839 *
840 * Ask through phone for a new connection to some service and block until
841 * success.
842 *
843 * @param mgmt Exchange management style.
844 * @param exch Exchange for sending the message.
845 * @param arg1 User defined argument.
846 * @param arg2 User defined argument.
847 * @param arg3 User defined argument.
848 *
849 * @return New session on success or NULL on error.
850 *
851 */
852async_sess_t *async_connect_me_to_blocking(exch_mgmt_t mgmt, async_exch_t *exch,
853 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
854{
855 if (exch == NULL) {
856 errno = ENOENT;
857 return NULL;
858 }
859
860 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
861 if (sess == NULL) {
862 errno = ENOMEM;
863 return NULL;
864 }
865
866 cap_phone_handle_t phone;
867 errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
868 IPC_FLAG_BLOCKING, &phone);
869
870 if (rc != EOK) {
871 errno = rc;
872 free(sess);
873 return NULL;
874 }
875
876 sess->iface = 0;
877 sess->mgmt = mgmt;
878 sess->phone = phone;
879 sess->arg1 = arg1;
880 sess->arg2 = arg2;
881 sess->arg3 = arg3;
882
883 fibril_mutex_initialize(&sess->remote_state_mtx);
884 sess->remote_state_data = NULL;
885
886 list_initialize(&sess->exch_list);
887 fibril_mutex_initialize(&sess->mutex);
888 atomic_set(&sess->refcnt, 0);
889
890 return sess;
891}
892
893/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
894 *
895 * Ask through phone for a new connection to some service and block until
896 * success.
897 *
898 * @param exch Exchange for sending the message.
899 * @param iface Connection interface.
900 * @param arg2 User defined argument.
901 * @param arg3 User defined argument.
902 *
903 * @return New session on success or NULL on error.
904 *
905 */
906async_sess_t *async_connect_me_to_blocking_iface(async_exch_t *exch, iface_t iface,
907 sysarg_t arg2, sysarg_t arg3)
908{
909 if (exch == NULL) {
910 errno = ENOENT;
911 return NULL;
912 }
913
914 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
915 if (sess == NULL) {
916 errno = ENOMEM;
917 return NULL;
918 }
919
920 cap_phone_handle_t phone;
921 errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
922 arg3, IPC_FLAG_BLOCKING, &phone);
923 if (rc != EOK) {
924 errno = rc;
925 free(sess);
926 return NULL;
927 }
928
929 sess->iface = iface;
930 sess->phone = phone;
931 sess->arg1 = iface;
932 sess->arg2 = arg2;
933 sess->arg3 = arg3;
934
935 fibril_mutex_initialize(&sess->remote_state_mtx);
936 sess->remote_state_data = NULL;
937
938 list_initialize(&sess->exch_list);
939 fibril_mutex_initialize(&sess->mutex);
940 atomic_set(&sess->refcnt, 0);
941
942 return sess;
943}
944
945/** Connect to a task specified by id.
946 *
947 */
948async_sess_t *async_connect_kbox(task_id_t id)
949{
950 async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
951 if (sess == NULL) {
952 errno = ENOMEM;
953 return NULL;
954 }
955
956 cap_phone_handle_t phone;
957 errno_t rc = ipc_connect_kbox(id, &phone);
958 if (rc != EOK) {
959 errno = rc;
960 free(sess);
961 return NULL;
962 }
963
964 sess->iface = 0;
965 sess->mgmt = EXCHANGE_ATOMIC;
966 sess->phone = phone;
967 sess->arg1 = 0;
968 sess->arg2 = 0;
969 sess->arg3 = 0;
970
971 fibril_mutex_initialize(&sess->remote_state_mtx);
972 sess->remote_state_data = NULL;
973
974 list_initialize(&sess->exch_list);
975 fibril_mutex_initialize(&sess->mutex);
976 atomic_set(&sess->refcnt, 0);
977
978 return sess;
979}
980
981static errno_t async_hangup_internal(cap_phone_handle_t phone)
982{
983 return ipc_hangup(phone);
984}
985
986/** Wrapper for ipc_hangup.
987 *
988 * @param sess Session to hung up.
989 *
990 * @return Zero on success or an error code.
991 *
992 */
993errno_t async_hangup(async_sess_t *sess)
994{
995 async_exch_t *exch;
996
997 assert(sess);
998
999 if (atomic_get(&sess->refcnt) > 0)
1000 return EBUSY;
1001
1002 fibril_mutex_lock(&async_sess_mutex);
1003
1004 errno_t rc = async_hangup_internal(sess->phone);
1005
1006 while (!list_empty(&sess->exch_list)) {
1007 exch = (async_exch_t *)
1008 list_get_instance(list_first(&sess->exch_list),
1009 async_exch_t, sess_link);
1010
1011 list_remove(&exch->sess_link);
1012 list_remove(&exch->global_link);
1013 async_hangup_internal(exch->phone);
1014 free(exch);
1015 }
1016
1017 free(sess);
1018
1019 fibril_mutex_unlock(&async_sess_mutex);
1020
1021 return rc;
1022}
1023
1024/** Start new exchange in a session.
1025 *
1026 * @param session Session.
1027 *
1028 * @return New exchange or NULL on error.
1029 *
1030 */
1031async_exch_t *async_exchange_begin(async_sess_t *sess)
1032{
1033 if (sess == NULL)
1034 return NULL;
1035
1036 exch_mgmt_t mgmt = sess->mgmt;
1037 if (sess->iface != 0)
1038 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
1039
1040 async_exch_t *exch = NULL;
1041
1042 fibril_mutex_lock(&async_sess_mutex);
1043
1044 if (!list_empty(&sess->exch_list)) {
1045 /*
1046 * There are inactive exchanges in the session.
1047 */
1048 exch = (async_exch_t *)
1049 list_get_instance(list_first(&sess->exch_list),
1050 async_exch_t, sess_link);
1051
1052 list_remove(&exch->sess_link);
1053 list_remove(&exch->global_link);
1054 } else {
1055 /*
1056 * There are no available exchanges in the session.
1057 */
1058
1059 if ((mgmt == EXCHANGE_ATOMIC) ||
1060 (mgmt == EXCHANGE_SERIALIZE)) {
1061 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
1062 if (exch != NULL) {
1063 link_initialize(&exch->sess_link);
1064 link_initialize(&exch->global_link);
1065 exch->sess = sess;
1066 exch->phone = sess->phone;
1067 }
1068 } else if (mgmt == EXCHANGE_PARALLEL) {
1069 cap_phone_handle_t phone;
1070 errno_t rc;
1071
1072 retry:
1073 /*
1074 * Make a one-time attempt to connect a new data phone.
1075 */
1076 rc = async_connect_me_to_internal(sess->phone, sess->arg1,
1077 sess->arg2, sess->arg3, 0, &phone);
1078 if (rc == EOK) {
1079 exch = (async_exch_t *) malloc(sizeof(async_exch_t));
1080 if (exch != NULL) {
1081 link_initialize(&exch->sess_link);
1082 link_initialize(&exch->global_link);
1083 exch->sess = sess;
1084 exch->phone = phone;
1085 } else
1086 async_hangup_internal(phone);
1087 } else if (!list_empty(&inactive_exch_list)) {
1088 /*
1089 * We did not manage to connect a new phone. But we
1090 * can try to close some of the currently inactive
1091 * connections in other sessions and try again.
1092 */
1093 exch = (async_exch_t *)
1094 list_get_instance(list_first(&inactive_exch_list),
1095 async_exch_t, global_link);
1096
1097 list_remove(&exch->sess_link);
1098 list_remove(&exch->global_link);
1099 async_hangup_internal(exch->phone);
1100 free(exch);
1101 goto retry;
1102 } else {
1103 /*
1104 * Wait for a phone to become available.
1105 */
1106 fibril_condvar_wait(&avail_phone_cv, &async_sess_mutex);
1107 goto retry;
1108 }
1109 }
1110 }
1111
1112 fibril_mutex_unlock(&async_sess_mutex);
1113
1114 if (exch != NULL) {
1115 atomic_inc(&sess->refcnt);
1116
1117 if (mgmt == EXCHANGE_SERIALIZE)
1118 fibril_mutex_lock(&sess->mutex);
1119 }
1120
1121 return exch;
1122}
1123
1124/** Finish an exchange.
1125 *
1126 * @param exch Exchange to finish.
1127 *
1128 */
1129void async_exchange_end(async_exch_t *exch)
1130{
1131 if (exch == NULL)
1132 return;
1133
1134 async_sess_t *sess = exch->sess;
1135 assert(sess != NULL);
1136
1137 exch_mgmt_t mgmt = sess->mgmt;
1138 if (sess->iface != 0)
1139 mgmt = sess->iface & IFACE_EXCHANGE_MASK;
1140
1141 atomic_dec(&sess->refcnt);
1142
1143 if (mgmt == EXCHANGE_SERIALIZE)
1144 fibril_mutex_unlock(&sess->mutex);
1145
1146 fibril_mutex_lock(&async_sess_mutex);
1147
1148 list_append(&exch->sess_link, &sess->exch_list);
1149 list_append(&exch->global_link, &inactive_exch_list);
1150 fibril_condvar_signal(&avail_phone_cv);
1151
1152 fibril_mutex_unlock(&async_sess_mutex);
1153}
1154
1155/** Wrapper for IPC_M_SHARE_IN calls using the async framework.
1156 *
1157 * @param exch Exchange for sending the message.
1158 * @param size Size of the destination address space area.
1159 * @param arg User defined argument.
1160 * @param flags Storage for the received flags. Can be NULL.
1161 * @param dst Address of the storage for the destination address space area
1162 * base address. Cannot be NULL.
1163 *
1164 * @return Zero on success or an error code from errno.h.
1165 *
1166 */
1167errno_t async_share_in_start(async_exch_t *exch, size_t size, sysarg_t arg,
1168 unsigned int *flags, void **dst)
1169{
1170 if (exch == NULL)
1171 return ENOENT;
1172
1173 sysarg_t _flags = 0;
1174 sysarg_t _dst = (sysarg_t) -1;
1175 errno_t res = async_req_2_4(exch, IPC_M_SHARE_IN, (sysarg_t) size,
1176 arg, NULL, &_flags, NULL, &_dst);
1177
1178 if (flags)
1179 *flags = (unsigned int) _flags;
1180
1181 *dst = (void *) _dst;
1182 return res;
1183}
1184
1185/** Wrapper for IPC_M_SHARE_OUT calls using the async framework.
1186 *
1187 * @param exch Exchange for sending the message.
1188 * @param src Source address space area base address.
1189 * @param flags Flags to be used for sharing. Bits can be only cleared.
1190 *
1191 * @return Zero on success or an error code from errno.h.
1192 *
1193 */
1194errno_t async_share_out_start(async_exch_t *exch, void *src, unsigned int flags)
1195{
1196 if (exch == NULL)
1197 return ENOENT;
1198
1199 return async_req_3_0(exch, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
1200 (sysarg_t) flags);
1201}
1202
1203/** Start IPC_M_DATA_READ using the async framework.
1204 *
1205 * @param exch Exchange for sending the message.
1206 * @param dst Address of the beginning of the destination buffer.
1207 * @param size Size of the destination buffer (in bytes).
1208 * @param dataptr Storage of call data (arg 2 holds actual data size).
1209 *
1210 * @return Hash of the sent message or 0 on error.
1211 *
1212 */
1213aid_t async_data_read(async_exch_t *exch, void *dst, size_t size,
1214 ipc_call_t *dataptr)
1215{
1216 return async_send_2(exch, IPC_M_DATA_READ, (sysarg_t) dst,
1217 (sysarg_t) size, dataptr);
1218}
1219
1220/** Wrapper for IPC_M_DATA_READ calls using the async framework.
1221 *
1222 * @param exch Exchange for sending the message.
1223 * @param dst Address of the beginning of the destination buffer.
1224 * @param size Size of the destination buffer.
1225 *
1226 * @return Zero on success or an error code from errno.h.
1227 *
1228 */
1229errno_t async_data_read_start(async_exch_t *exch, void *dst, size_t size)
1230{
1231 if (exch == NULL)
1232 return ENOENT;
1233
1234 return async_req_2_0(exch, IPC_M_DATA_READ, (sysarg_t) dst,
1235 (sysarg_t) size);
1236}
1237
1238/** Wrapper for IPC_M_DATA_WRITE calls using the async framework.
1239 *
1240 * @param exch Exchange for sending the message.
1241 * @param src Address of the beginning of the source buffer.
1242 * @param size Size of the source buffer.
1243 *
1244 * @return Zero on success or an error code from errno.h.
1245 *
1246 */
1247errno_t async_data_write_start(async_exch_t *exch, const void *src, size_t size)
1248{
1249 if (exch == NULL)
1250 return ENOENT;
1251
1252 return async_req_2_0(exch, IPC_M_DATA_WRITE, (sysarg_t) src,
1253 (sysarg_t) size);
1254}
1255
1256errno_t async_state_change_start(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
1257 sysarg_t arg3, async_exch_t *other_exch)
1258{
1259 return async_req_5_0(exch, IPC_M_STATE_CHANGE_AUTHORIZE,
1260 arg1, arg2, arg3, 0, CAP_HANDLE_RAW(other_exch->phone));
1261}
1262
1263/** Lock and get session remote state
1264 *
1265 * Lock and get the local replica of the remote state
1266 * in stateful sessions. The call should be paired
1267 * with async_remote_state_release*().
1268 *
1269 * @param[in] sess Stateful session.
1270 *
1271 * @return Local replica of the remote state.
1272 *
1273 */
1274void *async_remote_state_acquire(async_sess_t *sess)
1275{
1276 fibril_mutex_lock(&sess->remote_state_mtx);
1277 return sess->remote_state_data;
1278}
1279
1280/** Update the session remote state
1281 *
1282 * Update the local replica of the remote state
1283 * in stateful sessions. The remote state must
1284 * be already locked.
1285 *
1286 * @param[in] sess Stateful session.
1287 * @param[in] state New local replica of the remote state.
1288 *
1289 */
1290void async_remote_state_update(async_sess_t *sess, void *state)
1291{
1292 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
1293 sess->remote_state_data = state;
1294}
1295
1296/** Release the session remote state
1297 *
1298 * Unlock the local replica of the remote state
1299 * in stateful sessions.
1300 *
1301 * @param[in] sess Stateful session.
1302 *
1303 */
1304void async_remote_state_release(async_sess_t *sess)
1305{
1306 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
1307
1308 fibril_mutex_unlock(&sess->remote_state_mtx);
1309}
1310
1311/** Release the session remote state and end an exchange
1312 *
1313 * Unlock the local replica of the remote state
1314 * in stateful sessions. This is convenience function
1315 * which gets the session pointer from the exchange
1316 * and also ends the exchange.
1317 *
1318 * @param[in] exch Stateful session's exchange.
1319 *
1320 */
1321void async_remote_state_release_exchange(async_exch_t *exch)
1322{
1323 if (exch == NULL)
1324 return;
1325
1326 async_sess_t *sess = exch->sess;
1327 assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
1328
1329 async_exchange_end(exch);
1330 fibril_mutex_unlock(&sess->remote_state_mtx);
1331}
1332
1333void *async_as_area_create(void *base, size_t size, unsigned int flags,
1334 async_sess_t *pager, sysarg_t id1, sysarg_t id2, sysarg_t id3)
1335{
1336 as_area_pager_info_t pager_info = {
1337 .pager = pager->phone,
1338 .id1 = id1,
1339 .id2 = id2,
1340 .id3 = id3
1341 };
1342 return as_area_create(base, size, flags, &pager_info);
1343}
1344
1345/** @}
1346 */
Note: See TracBrowser for help on using the repository browser.