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

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

Use user-defined labels instead of phone hashes

This commit changes the way how the async framework maps incomming calls
to connections. Instead of abusing the kernel addresses of attached
phones as identifiers, the IPC_M_CONNECT_TO_ME and IPC_M_CONNECT_ME_TO
messages allow the server to specify an arbitrary label which is
remembered in the connected phone and consequently imprinted on each
call which is routed through this phone.

The async framework uses the address of the connection structure as the
label. This removes the need for a connection hash table because each
incoming call already remembers the connection in its label.

To disambiguate this new label and the other user-defined label used for
answers, the call structure now has the request_label member for the
former and answer_label member for the latter.

This commit also moves the kernel definition of ipc_data_t to abi/ and
removes the uspace redefinition thereof. Finally, when forwarding the
IPC_M_CONNECT_TO_ME call, the phone capability and the kernel object
allocated in request_process are now correctly disposed of.

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