source: mainline/libc/generic/async.c@ 8c6b45f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8c6b45f was 8c6b45f, checked in by Ondrej Palkovsky <ondrap@…>, 19 years ago

Fix gcc mips bug.

  • Property mode set to 100644
File size: 9.7 KB
RevLine 
[06502f7d]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.
[450cd3a]27 */
[06502f7d]28
[80649a91]29/**
30 * Asynchronous library
31 *
32 * The aim of this library is facilitating writing programs utilizing
33 * the asynchronous nature of Helenos IPC, yet using a normal way
34 * of programming.
35 *
36 * You should be able to write very simple multithreaded programs,
37 * the async framework will automatically take care of most synchronization
38 * problems.
39 *
40 * Default semantics:
41 * - send() - send asynchronously. If the kernel refuses to send more
42 * messages, [ try to get responses from kernel, if nothing
43 * found, might try synchronous ]
44 *
45 * Example of use:
46 *
47 * 1) Multithreaded client application
48 * create_thread(thread1);
49 * create_thread(thread2);
50 * ...
51 *
52 * thread1() {
53 * conn = ipc_connect_me_to();
54 * c1 = send(conn);
55 * c2 = send(conn);
56 * wait_for(c1);
57 * wait_for(c2);
58 * }
59 *
60 *
61 * 2) Multithreaded server application
62 * main() {
[53ca318]63 * async_manager();
[80649a91]64 * }
65 *
66 *
[53ca318]67 * client_connection(icallid, *icall) {
68 * if (want_refuse) {
69 * ipc_answer_fast(icallid, ELIMIT, 0, 0);
70 * return;
71 * }
72 * ipc_answer_fast(icallid, 0, 0, 0);
[80649a91]73 *
[53ca318]74 * callid = async_get_call(&call);
75 * handle(callid, call);
76 * ipc_answer_fast(callid, 1,2,3);
77 *
78 * callid = async_get_call(&call);
[80649a91]79 * ....
80 * }
[a2cd194]81 *
[8bc924e]82 * TODO: Detaching/joining dead psthreads?
[80649a91]83 */
84#include <futex.h>
85#include <async.h>
86#include <psthread.h>
87#include <stdio.h>
88#include <libadt/hash_table.h>
89#include <libadt/list.h>
90#include <ipc/ipc.h>
91#include <assert.h>
92#include <errno.h>
93
94static atomic_t conn_futex = FUTEX_INITIALIZER;
95static hash_table_t conn_hash_table;
96
97typedef struct {
98 link_t link;
99 ipc_callid_t callid;
100 ipc_call_t call;
101} msg_t;
102
103typedef struct {
104 link_t link;
105 ipcarg_t in_phone_hash; /**< Incoming phone hash. */
106 link_t msg_queue; /**< Messages that should be delivered to this thread */
107 pstid_t ptid; /**< Thread associated with this connection */
108 int active; /**< If this thread is currently active */
109 /* Structures for connection opening packet */
110 ipc_callid_t callid;
111 ipc_call_t call;
[53ca318]112 void (*cthread)(ipc_callid_t,ipc_call_t *);
[80649a91]113} connection_t;
114
115__thread connection_t *PS_connection;
116
117/* Hash table functions */
118
[a2cd194]119#define CONN_HASH_TABLE_CHAINS 32
[80649a91]120
121static hash_index_t conn_hash(unsigned long *key)
[450cd3a]122{
[80649a91]123 assert(key);
[a2cd194]124 return ((*key) >> 4) % CONN_HASH_TABLE_CHAINS;
[450cd3a]125}
[06502f7d]126
[80649a91]127static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
[450cd3a]128{
[80649a91]129 connection_t *hs;
130
131 hs = hash_table_get_instance(item, connection_t, link);
132
133 return key[0] == hs->in_phone_hash;
[450cd3a]134}
[06502f7d]135
[80649a91]136static void conn_remove(link_t *item)
[450cd3a]137{
[80649a91]138 free(hash_table_get_instance(item, connection_t, link));
[450cd3a]139}
140
[80649a91]141
142/** Operations for NS hash table. */
143static hash_table_operations_t conn_hash_table_ops = {
144 .hash = conn_hash,
145 .compare = conn_compare,
146 .remove_callback = conn_remove
147};
148
149/** Try to route a call to an appropriate connection thread
150 *
151 */
152static int route_call(ipc_callid_t callid, ipc_call_t *call)
[450cd3a]153{
[80649a91]154 connection_t *conn;
155 msg_t *msg;
156 link_t *hlp;
157 unsigned long key;
158
159 futex_down(&conn_futex);
160
161 key = call->in_phone_hash;
162 hlp = hash_table_find(&conn_hash_table, &key);
163 if (!hlp) {
164 futex_up(&conn_futex);
165 return 0;
[450cd3a]166 }
[80649a91]167 conn = hash_table_get_instance(hlp, connection_t, link);
168
169 msg = malloc(sizeof(*msg));
170 msg->callid = callid;
171 msg->call = *call;
172 list_append(&msg->link, &conn->msg_queue);
173
174 if (!conn->active) {
175 conn->active = 1;
176 psthread_add_ready(conn->ptid);
177 }
178
179 futex_up(&conn_futex);
180
181 return 1;
182}
183
[a2cd194]184/** Return new incoming message for current(thread-local) connection */
[80649a91]185ipc_callid_t async_get_call(ipc_call_t *call)
186{
187 msg_t *msg;
188 ipc_callid_t callid;
189 connection_t *conn;
190
191 futex_down(&conn_futex);
192
193 conn = PS_connection;
194 /* If nothing in queue, wait until something appears */
195 if (list_empty(&conn->msg_queue)) {
196 conn->active = 0;
197 psthread_schedule_next_adv(PS_TO_MANAGER);
[450cd3a]198 }
199
[80649a91]200 msg = list_get_instance(conn->msg_queue.next, msg_t, link);
201 list_remove(&msg->link);
202 callid = msg->callid;
203 *call = msg->call;
204 free(msg);
205
206 futex_up(&conn_futex);
207 return callid;
208}
209
[a2cd194]210/** Thread function that gets created on new connection
211 *
212 * This function is defined as a weak symbol - to be redefined in
213 * user code.
214 */
[80649a91]215void client_connection(ipc_callid_t callid, ipc_call_t *call)
216{
[a2cd194]217 ipc_answer_fast(callid, ENOENT, 0, 0);
[80649a91]218}
219
[a2cd194]220/** Wrapper for client connection thread
221 *
222 * When new connection arrives, thread with this function is created.
223 * It calls client_connection and does final cleanup.
224 *
225 * @parameter arg Connection structure pointer
226 */
[80649a91]227static int connection_thread(void *arg)
228{
[a2cd194]229 unsigned long key;
230 msg_t *msg;
[8c6b45f]231 connection_t *conn;
[a2cd194]232
[80649a91]233 /* Setup thread local connection pointer */
234 PS_connection = (connection_t *)arg;
[8c6b45f]235 conn = PS_connection;
236 conn->cthread(conn->callid, &conn->call);
[80649a91]237
[a2cd194]238 /* Remove myself from connection hash table */
[80649a91]239 futex_down(&conn_futex);
[8c6b45f]240 key = conn->in_phone_hash;
[a2cd194]241 hash_table_remove(&conn_hash_table, &key, 1);
[80649a91]242 futex_up(&conn_futex);
[a2cd194]243 /* Answer all remaining messages with ehangup */
[8c6b45f]244 while (!list_empty(&conn->msg_queue)) {
245 msg = list_get_instance(conn->msg_queue.next, msg_t, link);
[a2cd194]246 list_remove(&msg->link);
247 ipc_answer_fast(msg->callid, EHANGUP, 0, 0);
248 free(msg);
249 }
[80649a91]250}
251
252/** Create new thread for a new connection
253 *
254 * Creates new thread for connection, fills in connection
255 * structures and inserts it into the hash table, so that
256 * later we can easily do routing of messages to particular
257 * threads.
[53ca318]258 *
259 * @param callid Callid of the IPC_M_CONNECT_ME_TO packet
260 * @param call Call data of the opening packet
261 * @param cthread Thread function that should be called upon
262 * opening the connection
263 * @return New thread id
[80649a91]264 */
[53ca318]265pstid_t async_new_connection(ipc_callid_t callid, ipc_call_t *call,
266 void (*cthread)(ipc_callid_t,ipc_call_t *))
[80649a91]267{
268 pstid_t ptid;
269 connection_t *conn;
270 unsigned long key;
271
272 conn = malloc(sizeof(*conn));
273 if (!conn) {
274 ipc_answer_fast(callid, ENOMEM, 0, 0);
[53ca318]275 return NULL;
[80649a91]276 }
277 conn->in_phone_hash = IPC_GET_ARG3(*call);
278 list_initialize(&conn->msg_queue);
279 conn->ptid = psthread_create(connection_thread, conn);
280 conn->callid = callid;
281 conn->call = *call;
282 conn->active = 1; /* We will activate it asap */
[53ca318]283 conn->cthread = cthread;
[80649a91]284 list_initialize(&conn->link);
285 if (!conn->ptid) {
286 free(conn);
287 ipc_answer_fast(callid, ENOMEM, 0, 0);
[53ca318]288 return NULL;
[80649a91]289 }
290 key = conn->in_phone_hash;
291 futex_down(&conn_futex);
292 /* Add connection to hash table */
293 hash_table_insert(&conn_hash_table, &key, &conn->link);
294 futex_up(&conn_futex);
295
296 psthread_add_ready(conn->ptid);
[53ca318]297
298 return conn->ptid;
[80649a91]299}
300
301/** Handle call to a task */
302static void handle_call(ipc_callid_t callid, ipc_call_t *call)
303{
304 if (route_call(callid, call))
305 return;
306
307 switch (IPC_GET_METHOD(*call)) {
308 case IPC_M_INTERRUPT:
309 break;
310 case IPC_M_CONNECT_ME_TO:
311 /* Open new connection with thread etc. */
[53ca318]312 async_new_connection(callid, call, client_connection);
[80649a91]313 break;
314 default:
315 ipc_answer_fast(callid, EHANGUP, 0, 0);
316 }
[450cd3a]317}
318
[80649a91]319/** Endless loop dispatching incoming calls and answers */
320int async_manager()
321{
322 ipc_call_t call;
323 ipc_callid_t callid;
324
325 while (1) {
326 if (psthread_schedule_next_adv(PS_FROM_MANAGER)) {
327 futex_up(&conn_futex); /* conn_futex is always held
328 * when entering manager thread
329 */
330 continue;
331 }
332 callid = ipc_wait_cycle(&call,SYNCH_NO_TIMEOUT,SYNCH_BLOCKING);
333
334 if (callid & IPC_CALLID_ANSWERED)
335 continue;
336 handle_call(callid, &call);
337 }
338}
339
[a2cd194]340/** Function to start async_manager as a standalone thread
341 *
342 * When more kernel threads are used, one async manager should
343 * exist per thread. The particular implementation may change,
344 * currently one async_manager is started automatically per kernel
345 * thread except main thread.
346 */
[80649a91]347static int async_manager_thread(void *arg)
348{
349 futex_up(&conn_futex); /* conn_futex is always locked when entering
350 * manager */
351 async_manager();
352}
[450cd3a]353
[80649a91]354/** Add one manager to manager list */
355void async_create_manager(void)
[450cd3a]356{
[80649a91]357 pstid_t ptid;
358
359 ptid = psthread_create(async_manager_thread, NULL);
360 psthread_add_manager(ptid);
361}
362
363/** Remove one manager from manager list */
364void async_destroy_manager(void)
365{
366 psthread_remove_manager();
367}
368
369/** Initialize internal structures needed for async manager */
370int _async_init(void)
371{
[a2cd194]372 if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1, &conn_hash_table_ops)) {
[80649a91]373 printf("%s: cannot create hash table\n", "async");
374 return ENOMEM;
375 }
376
[450cd3a]377}
Note: See TracBrowser for help on using the repository browser.