source: mainline/uspace/lib/c/generic/async_sess.c@ 311bc25

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

If there are no available phones to be used by the async sessions
framework, sleep on a condition variable until a phone becomes
available.

  • Property mode set to 100644
File size: 10.3 KB
RevLine 
[7907cf9]1/*
2 * Copyright (c) 2010 Jakub Jermar
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 * This file implements simple session support for the async framework.
37 *
38 * By the term 'session', we mean a logical data path between a client and a
[d3a1ade3]39 * server over which the client can perform multiple concurrent exchanges.
40 * Each exchange consists of one or more requests (IPC calls) which can
[7907cf9]41 * be potentially blocking.
42 *
43 * Clients and servers are naturally connected using IPC phones, thus an IPC
44 * phone represents a session between a client and a server. In one
[d3a1ade3]45 * session, there can be many outstanding exchanges. In the current
46 * implementation each concurrent exchanges takes place over a different
47 * connection (there can be at most one active exchage per connection).
[7907cf9]48 *
49 * Sessions make it useful for a client or client API to support concurrent
50 * requests, independent of the actual implementation. Sessions provide
51 * an abstract interface to concurrent IPC communication. This is especially
52 * useful for client API stubs that aim to be reentrant (i.e. that allow
53 * themselves to be called from different fibrils and threads concurrently).
54 *
55 * There are several possible implementations of sessions. This implementation
56 * uses additional phones to represent sessions. Using phones both for the
[d3a1ade3]57 * session and also for its exchages/connections has several advantages:
[7907cf9]58 *
[d3a1ade3]59 * - to make a series of exchanges over a session, the client can continue to
[7907cf9]60 * use the existing async framework APIs
61 * - the server supports sessions by the virtue of spawning a new connection
62 * fibril, just as it does for every new connection even without sessions
63 * - the implementation is pretty straightforward; a very naive implementation
[d3a1ade3]64 * would be to make each exchage using a fresh phone (that is what we
[7907cf9]65 * have done in the past); a slightly better approach would be to cache
[d3a1ade3]66 * connections so that they can be reused by a later exchange within
[7907cf9]67 * the same session (that is what this implementation does)
68 *
69 * The main disadvantages of using phones to represent sessions are:
70 *
[d3a1ade3]71 * - if there are too many exchanges (even cached ones), the task may hit its
[7907cf9]72 * limit on the maximum number of connected phones, which could prevent the
73 * task from making new IPC connections to other tasks
74 * - if there are too many IPC connections already, it may be impossible to
[d3a1ade3]75 * create an exchange by connecting a new phone thanks to the task's limit on
[7907cf9]76 * the maximum number of connected phones
77 *
78 * These problems can be alleviated by increasing the limit on the maximum
79 * number of connected phones to some reasonable value and by limiting the number
80 * of cached connections to some fraction of this limit.
81 *
82 * The cache itself has a mechanism to close some number of unused phones if a
83 * new phone cannot be connected, but the outer world currently does not have a
84 * way to ask the phone cache to shrink.
85 *
86 * To minimize the confusion stemming from the fact that we use phones for two
87 * things (the session itself and also one for each data connection), this file
88 * makes the distinction by using the term 'session phone' for the former and
89 * 'data phone' for the latter. Under the hood, all phones remain equal,
90 * of course.
91 *
92 * There is a small inefficiency in that the cache repeatedly allocates and
93 * deallocates the conn_node_t structures when in fact it could keep the
94 * allocated structures around and reuse them later. But such a solution would
95 * be effectively implementing a poor man's slab allocator while it would be
96 * better to have the slab allocator ported to uspace so that everyone could
97 * benefit from it.
98 */
99
100#include <async_sess.h>
101#include <ipc/ipc.h>
102#include <fibril_synch.h>
103#include <adt/list.h>
104#include <adt/hash_table.h>
105#include <malloc.h>
106#include <errno.h>
107#include <assert.h>
108
[8cd21d16]109/** An inactive open connection. */
[7907cf9]110typedef struct {
[8cd21d16]111 link_t sess_link; /**< Link for the session list of inactive connections. */
[8b5c8ae]112 link_t global_link; /**< Link for the global list of inactive connections. */
[7907cf9]113 int data_phone; /**< Connected data phone. */
114} conn_node_t;
115
116/**
[8b5c8ae]117 * Mutex protecting the inactive_conn_head list, the session list and the
118 * avail_phone condition variable.
[7907cf9]119 */
120static fibril_mutex_t async_sess_mutex;
121
122/**
123 * List of all currently inactive connections.
124 */
125static LIST_INITIALIZE(inactive_conn_head);
126
127/**
[a1dc7fe]128 * List of all open sessions.
[7907cf9]129 */
[a1dc7fe]130static LIST_INITIALIZE(session_list_head);
[7907cf9]131
[8b5c8ae]132/**
133 * Condition variable used to wait for a phone to become available.
134 */
135static FIBRIL_CONDVAR_INITIALIZE(avail_phone_cv);
136
[7907cf9]137/** Initialize the async_sess subsystem.
138 *
139 * Needs to be called prior to any other interface in this file.
140 */
[c1c0184]141void _async_sess_init(void)
[7907cf9]142{
143 fibril_mutex_initialize(&async_sess_mutex);
144 list_initialize(&inactive_conn_head);
[a1dc7fe]145 list_initialize(&session_list_head);
[7907cf9]146}
147
[8cd21d16]148/** Create a session.
149 *
150 * Session is a logical datapath from a client task to a server task.
151 * One session can accomodate multiple concurrent exchanges. Here
152 * @a phone is a phone connected to the desired server task.
153 *
154 * This function always succeeds.
155 *
156 * @param sess Session structure provided by caller, will be filled in.
157 * @param phone Phone connected to the desired server task.
[9d12059]158 * @param arg1 Value to pass as first argument upon creating a new
159 * connection. Typical use is to identify a resource within
160 * the server that the caller wants to access (port ID,
161 * interface ID, device ID, etc.).
[8cd21d16]162 */
[9d12059]163void async_session_create(async_sess_t *sess, int phone, sysarg_t arg1)
[7907cf9]164{
[c1c0184]165 sess->sess_phone = phone;
[9d12059]166 sess->connect_arg1 = arg1;
[7907cf9]167 list_initialize(&sess->conn_head);
[a1dc7fe]168
169 /* Add to list of sessions. */
170 fibril_mutex_lock(&async_sess_mutex);
171 list_append(&sess->sess_link, &session_list_head);
172 fibril_mutex_unlock(&async_sess_mutex);
[7907cf9]173}
174
[8cd21d16]175/** Destroy a session.
176 *
177 * Dismantle session structure @a sess and release any resources (connections)
178 * held by the session.
179 *
180 * @param sess Session to destroy.
181 */
[c1c0184]182void async_session_destroy(async_sess_t *sess)
183{
[d1c041a]184 conn_node_t *conn;
185
[a1dc7fe]186 /* Remove from list of sessions. */
187 fibril_mutex_lock(&async_sess_mutex);
188 list_remove(&sess->sess_link);
189 fibril_mutex_unlock(&async_sess_mutex);
190
[d1c041a]191 /* We did not connect the phone so we do not hang it up either. */
[c1c0184]192 sess->sess_phone = -1;
[d1c041a]193
194 /* Tear down all data connections. */
195 while (!list_empty(&sess->conn_head)) {
196 conn = list_get_instance(sess->conn_head.next, conn_node_t,
[8cd21d16]197 sess_link);
[d1c041a]198
[8cd21d16]199 list_remove(&conn->sess_link);
[d1c041a]200 list_remove(&conn->global_link);
201
202 ipc_hangup(conn->data_phone);
203 free(conn);
204 }
[8b5c8ae]205
206 fibril_condvar_broadcast(&avail_phone_cv);
[c1c0184]207}
208
[7907cf9]209static void conn_node_initialize(conn_node_t *conn)
210{
[8cd21d16]211 link_initialize(&conn->sess_link);
[7907cf9]212 link_initialize(&conn->global_link);
213 conn->data_phone = -1;
214}
215
[d3a1ade3]216/** Start new exchange in a session.
[7907cf9]217 *
[c1c0184]218 * @param sess_phone Session.
[d3a1ade3]219 * @return Phone representing the new exchange or a negative error
[7907cf9]220 * code.
221 */
[d3a1ade3]222int async_exchange_begin(async_sess_t *sess)
[7907cf9]223{
224 conn_node_t *conn;
225 int data_phone;
226
227 fibril_mutex_lock(&async_sess_mutex);
228
229 if (!list_empty(&sess->conn_head)) {
230 /*
231 * There are inactive connections in the session.
232 */
233 conn = list_get_instance(sess->conn_head.next, conn_node_t,
[8cd21d16]234 sess_link);
235 list_remove(&conn->sess_link);
[7907cf9]236 list_remove(&conn->global_link);
237
238 data_phone = conn->data_phone;
239 free(conn);
240 } else {
241 /*
242 * There are no available connections in the session.
243 * Make a one-time attempt to connect a new data phone.
244 */
245retry:
[339dfc5]246 data_phone = async_connect_me_to(sess->sess_phone,
247 sess->connect_arg1, 0, 0);
[7907cf9]248 if (data_phone >= 0) {
249 /* success, do nothing */
250 } else if (!list_empty(&inactive_conn_head)) {
251 /*
252 * We did not manage to connect a new phone. But we can
253 * try to close some of the currently inactive
254 * connections in other sessions and try again.
255 */
256 conn = list_get_instance(inactive_conn_head.next,
257 conn_node_t, global_link);
258 list_remove(&conn->global_link);
[8cd21d16]259 list_remove(&conn->sess_link);
[7907cf9]260 data_phone = conn->data_phone;
261 free(conn);
262 ipc_hangup(data_phone);
263 goto retry;
264 } else {
265 /*
[8b5c8ae]266 * Wait for a phone to become available.
[7907cf9]267 */
[8b5c8ae]268 fibril_condvar_wait(&avail_phone_cv, &async_sess_mutex);
269 goto retry;
[7907cf9]270 }
271 }
272
273 fibril_mutex_unlock(&async_sess_mutex);
274 return data_phone;
275}
276
[d3a1ade3]277/** Finish an exchange.
[7907cf9]278 *
[c1c0184]279 * @param sess Session.
[d3a1ade3]280 * @param data_phone Phone representing the exchange within the session.
[7907cf9]281 */
[d3a1ade3]282void async_exchange_end(async_sess_t *sess, int data_phone)
[7907cf9]283{
284 conn_node_t *conn;
285
286 fibril_mutex_lock(&async_sess_mutex);
[8b5c8ae]287 fibril_condvar_signal(&avail_phone_cv);
[7907cf9]288 conn = (conn_node_t *) malloc(sizeof(conn_node_t));
289 if (!conn) {
290 /*
291 * Being unable to remember the connected data phone here
292 * means that we simply hang up.
293 */
294 ipc_hangup(data_phone);
[8b5c8ae]295 fibril_mutex_unlock(&async_sess_mutex);
[7907cf9]296 return;
297 }
298
299 conn_node_initialize(conn);
300 conn->data_phone = data_phone;
[8cd21d16]301 list_append(&conn->sess_link, &sess->conn_head);
[7907cf9]302 list_append(&conn->global_link, &inactive_conn_head);
303 fibril_mutex_unlock(&async_sess_mutex);
304}
305
306/** @}
307 */
Note: See TracBrowser for help on using the repository browser.