Subversion Repositories HelenOS

Rev

Rev 2488 | Rev 2492 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 2488 Rev 2490
1
/*
1
/*
2
 * Copyright (c) 2006 Ondrej Palkovsky
2
 * Copyright (c) 2006 Ondrej Palkovsky
3
 * All rights reserved.
3
 * All rights reserved.
4
 *
4
 *
5
 * Redistribution and use in source and binary forms, with or without
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
6
 * modification, are permitted provided that the following conditions
7
 * are met:
7
 * are met:
8
 *
8
 *
9
 * - Redistributions of source code must retain the above copyright
9
 * - Redistributions of source code must retain the above copyright
10
 *   notice, this list of conditions and the following disclaimer.
10
 *   notice, this list of conditions and the following disclaimer.
11
 * - Redistributions in binary form must reproduce the above copyright
11
 * - Redistributions in binary form must reproduce the above copyright
12
 *   notice, this list of conditions and the following disclaimer in the
12
 *   notice, this list of conditions and the following disclaimer in the
13
 *   documentation and/or other materials provided with the distribution.
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
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.
15
 *   derived from this software without specific prior written permission.
16
 *
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
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
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
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
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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
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.
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
27
 */
28
 
28
 
29
/** @addtogroup libc
29
/** @addtogroup libc
30
 * @{
30
 * @{
31
 */
31
 */
32
/** @file
32
/** @file
33
 */
33
 */
34
 
34
 
35
/**
35
/**
36
 * Asynchronous library
36
 * Asynchronous library
37
 *
37
 *
38
 * The aim of this library is facilitating writing programs utilizing the
38
 * The aim of this library is facilitating writing programs utilizing the
39
 * asynchronous nature of HelenOS IPC, yet using a normal way of programming.
39
 * asynchronous nature of HelenOS IPC, yet using a normal way of programming.
40
 *
40
 *
41
 * You should be able to write very simple multithreaded programs, the async
41
 * You should be able to write very simple multithreaded programs, the async
42
 * framework will automatically take care of most synchronization problems.
42
 * framework will automatically take care of most synchronization problems.
43
 *
43
 *
44
 * Default semantics:
44
 * Default semantics:
45
 * - async_send_*():    send asynchronously. If the kernel refuses to send
45
 * - async_send_*():    send asynchronously. If the kernel refuses to send
46
 *          more messages, [ try to get responses from kernel, if
46
 *          more messages, [ try to get responses from kernel, if
47
 *          nothing found, might try synchronous ]
47
 *          nothing found, might try synchronous ]
48
 *
48
 *
49
 * Example of use (pseudo C):
49
 * Example of use (pseudo C):
50
 *
50
 *
51
 * 1) Multithreaded client application
51
 * 1) Multithreaded client application
52
 *
52
 *
53
 * fibril_create(fibril1, ...);
53
 * fibril_create(fibril1, ...);
54
 * fibril_create(fibril2, ...);
54
 * fibril_create(fibril2, ...);
55
 * ...
55
 * ...
56
 *  
56
 *  
57
 * int fibril1(void *arg)
57
 * int fibril1(void *arg)
58
 * {
58
 * {
59
 *  conn = ipc_connect_me_to();
59
 *  conn = ipc_connect_me_to();
60
 *  c1 = async_send(conn);
60
 *  c1 = async_send(conn);
61
 *  c2 = async_send(conn);
61
 *  c2 = async_send(conn);
62
 *  async_wait_for(c1);
62
 *  async_wait_for(c1);
63
 *  async_wait_for(c2);
63
 *  async_wait_for(c2);
64
 *  ...
64
 *  ...
65
 * }
65
 * }
66
 *
66
 *
67
 *
67
 *
68
 * 2) Multithreaded server application
68
 * 2) Multithreaded server application
69
 * main()
69
 * main()
70
 * {
70
 * {
71
 *  async_manager();
71
 *  async_manager();
72
 * }
72
 * }
73
 *
73
 *
74
 *
74
 *
75
 * client_connection(icallid, *icall)
75
 * my_client_connection(icallid, *icall)
76
 * {
76
 * {
77
 *  if (want_refuse) {
77
 *  if (want_refuse) {
78
 *      ipc_answer_fast(icallid, ELIMIT, 0, 0);
78
 *      ipc_answer_fast(icallid, ELIMIT, 0, 0);
79
 *      return;
79
 *      return;
80
 *  }
80
 *  }
81
 *  ipc_answer_fast(icallid, EOK, 0, 0);
81
 *  ipc_answer_fast(icallid, EOK, 0, 0);
82
 *
82
 *
83
 *  callid = async_get_call(&call);
83
 *  callid = async_get_call(&call);
84
 *  handle_call(callid, call);
84
 *  handle_call(callid, call);
85
 *  ipc_answer_fast(callid, 1, 2, 3);
85
 *  ipc_answer_fast(callid, 1, 2, 3);
86
 *
86
 *
87
 *  callid = async_get_call(&call);
87
 *  callid = async_get_call(&call);
88
 *  ....
88
 *  ....
89
 * }
89
 * }
90
 *
90
 *
91
 */
91
 */
92
 
92
 
93
#include <futex.h>
93
#include <futex.h>
94
#include <async.h>
94
#include <async.h>
95
#include <fibril.h>
95
#include <fibril.h>
96
#include <stdio.h>
96
#include <stdio.h>
97
#include <libadt/hash_table.h>
97
#include <libadt/hash_table.h>
98
#include <libadt/list.h>
98
#include <libadt/list.h>
99
#include <ipc/ipc.h>
99
#include <ipc/ipc.h>
100
#include <assert.h>
100
#include <assert.h>
101
#include <errno.h>
101
#include <errno.h>
102
#include <sys/time.h>
102
#include <sys/time.h>
103
#include <arch/barrier.h>
103
#include <arch/barrier.h>
104
 
104
 
105
atomic_t async_futex = FUTEX_INITIALIZER;
105
atomic_t async_futex = FUTEX_INITIALIZER;
106
static hash_table_t conn_hash_table;
106
static hash_table_t conn_hash_table;
107
static LIST_INITIALIZE(timeout_list);
107
static LIST_INITIALIZE(timeout_list);
108
 
108
 
109
/** Structures of this type represent a waiting fibril. */
109
/** Structures of this type represent a waiting fibril. */
110
typedef struct {
110
typedef struct {
111
    /** Expiration time. */
111
    /** Expiration time. */
112
    struct timeval expires;    
112
    struct timeval expires;    
113
    /** If true, this struct is in the timeout list. */
113
    /** If true, this struct is in the timeout list. */
114
    int inlist;
114
    int inlist;
115
    /** Timeout list link. */
115
    /** Timeout list link. */
116
    link_t link;
116
    link_t link;
117
 
117
 
118
    /** Fibril waiting for this message. */
118
    /** Identification of and link to the waiting fibril. */
119
    fid_t fid;
119
    fid_t fid;
120
    /** If true, this fibril is currently active. */
120
    /** If true, this fibril is currently active. */
121
    int active;
121
    int active;
122
    /** If true, we have timed out. */
122
    /** If true, we have timed out. */
123
    int timedout;
123
    int timedout;
124
} awaiter_t;
124
} awaiter_t;
125
 
125
 
126
typedef struct {
126
typedef struct {
127
    awaiter_t wdata;
127
    awaiter_t wdata;
128
   
128
   
129
    /** If reply was received. */
129
    /** If reply was received. */
130
    int done;
130
    int done;
131
    /** Pointer to where the answer data is stored. */
131
    /** Pointer to where the answer data is stored. */
132
    ipc_call_t *dataptr;
132
    ipc_call_t *dataptr;
133
 
133
 
134
    ipcarg_t retval;
134
    ipcarg_t retval;
135
} amsg_t;
135
} amsg_t;
136
 
136
 
-
 
137
/**
-
 
138
 * Structures of this type are used to group information about a call and a
-
 
139
 * message queue link.
-
 
140
 */
137
typedef struct {
141
typedef struct {
138
    link_t link;
142
    link_t link;
139
    ipc_callid_t callid;
143
    ipc_callid_t callid;
140
    ipc_call_t call;
144
    ipc_call_t call;
141
} msg_t;
145
} msg_t;
142
 
146
 
143
typedef struct {
147
typedef struct {
144
    awaiter_t wdata;
148
    awaiter_t wdata;
145
 
149
 
146
    /** Hash table link. */
150
    /** Hash table link. */
147
    link_t link;
151
    link_t link;
148
 
152
 
149
    /** Incoming phone hash. */
153
    /** Incoming phone hash. */
150
    ipcarg_t in_phone_hash;    
154
    ipcarg_t in_phone_hash;    
151
 
155
 
152
    /** Messages that should be delivered to this fibril. */
156
    /** Messages that should be delivered to this fibril. */
153
    link_t msg_queue;      
157
    link_t msg_queue;      
154
                     
158
                     
155
    /** Identification of the opening call. */
159
    /** Identification of the opening call. */
156
    ipc_callid_t callid;
160
    ipc_callid_t callid;
157
    /** Call data of the opening call. */
161
    /** Call data of the opening call. */
158
    ipc_call_t call;
162
    ipc_call_t call;
159
 
163
 
160
    /** Identification of the closing call. */
164
    /** Identification of the closing call. */
161
    ipc_callid_t close_callid;
165
    ipc_callid_t close_callid;
162
 
166
 
163
    /** Fibril function that will be used to handle the connection. */
167
    /** Fibril function that will be used to handle the connection. */
164
    void (*cfibril)(ipc_callid_t, ipc_call_t *);
168
    void (*cfibril)(ipc_callid_t, ipc_call_t *);
165
} connection_t;
169
} connection_t;
166
 
170
 
167
/** Identifier of the incoming connection handled by the current fibril. */
171
/** Identifier of the incoming connection handled by the current fibril. */
168
__thread connection_t *FIBRIL_connection;
172
__thread connection_t *FIBRIL_connection;
169
 
173
 
-
 
174
/**
170
/** If true, it is forbidden to use async_req functions and all preemption is
175
 * If true, it is forbidden to use async_req functions and all preemption is
171
 * disabled. */
176
 * disabled.
-
 
177
 */
172
__thread int in_interrupt_handler;
178
__thread int in_interrupt_handler;
173
 
179
 
174
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call);
180
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call);
175
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call);
181
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call);
-
 
182
 
-
 
183
/**
-
 
184
 * Pointer to a fibril function that will be used to handle connections.
-
 
185
 */
176
static async_client_conn_t client_connection = default_client_connection;
186
static async_client_conn_t client_connection = default_client_connection;
-
 
187
/**
-
 
188
 * Pointer to a fibril function that will be used to handle interrupt
-
 
189
 * notifications.
-
 
190
 */
177
static async_client_conn_t interrupt_received = default_interrupt_received;
191
static async_client_conn_t interrupt_received = default_interrupt_received;
178
 
192
 
179
#define CONN_HASH_TABLE_CHAINS  32
193
#define CONN_HASH_TABLE_CHAINS  32
180
 
194
 
181
/** Compute hash into the connection hash table based on the source phone hash.
195
/** Compute hash into the connection hash table based on the source phone hash.
182
 *
196
 *
183
 * @param key       Pointer to source phone hash.
197
 * @param key       Pointer to source phone hash.
184
 *
198
 *
185
 * @return      Index into the connection hash table.
199
 * @return      Index into the connection hash table.
186
 */
200
 */
187
static hash_index_t conn_hash(unsigned long *key)
201
static hash_index_t conn_hash(unsigned long *key)
188
{
202
{
189
    assert(key);
203
    assert(key);
190
    return ((*key) >> 4) % CONN_HASH_TABLE_CHAINS;
204
    return ((*key) >> 4) % CONN_HASH_TABLE_CHAINS;
191
}
205
}
192
 
206
 
193
/** Compare hash table item with a key.
207
/** Compare hash table item with a key.
194
 *
208
 *
195
 * @param key       Array containing the source phone hash as the only item.
209
 * @param key       Array containing the source phone hash as the only item.
196
 * @param keys      Expected 1 but ignored.
210
 * @param keys      Expected 1 but ignored.
197
 * @param item      Connection hash table item.
211
 * @param item      Connection hash table item.
198
 *
212
 *
199
 * @return      True on match, false otherwise.
213
 * @return      True on match, false otherwise.
200
 */
214
 */
201
static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
215
static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
202
{
216
{
203
    connection_t *hs;
217
    connection_t *hs;
204
 
218
 
205
    hs = hash_table_get_instance(item, connection_t, link);
219
    hs = hash_table_get_instance(item, connection_t, link);
206
   
220
   
207
    return key[0] == hs->in_phone_hash;
221
    return key[0] == hs->in_phone_hash;
208
}
222
}
209
 
223
 
210
/** Connection hash table removal callback function.
224
/** Connection hash table removal callback function.
211
 *
225
 *
212
 * This function is called whenever a connection is removed from the connection
226
 * This function is called whenever a connection is removed from the connection
213
 * hash table.
227
 * hash table.
214
 *
228
 *
215
 * @param item      Connection hash table item being removed.
229
 * @param item      Connection hash table item being removed.
216
 */
230
 */
217
static void conn_remove(link_t *item)
231
static void conn_remove(link_t *item)
218
{
232
{
219
    free(hash_table_get_instance(item, connection_t, link));
233
    free(hash_table_get_instance(item, connection_t, link));
220
}
234
}
221
 
235
 
222
 
236
 
223
/** Operations for the connection hash table. */
237
/** Operations for the connection hash table. */
224
static hash_table_operations_t conn_hash_table_ops = {
238
static hash_table_operations_t conn_hash_table_ops = {
225
    .hash = conn_hash,
239
    .hash = conn_hash,
226
    .compare = conn_compare,
240
    .compare = conn_compare,
227
    .remove_callback = conn_remove
241
    .remove_callback = conn_remove
228
};
242
};
229
 
243
 
230
/** Sort in current fibril's timeout request.
244
/** Sort in current fibril's timeout request.
231
 *
245
 *
232
 * @param wd        Wait data of the current fibril.
246
 * @param wd        Wait data of the current fibril.
233
 */
247
 */
234
static void insert_timeout(awaiter_t *wd)
248
static void insert_timeout(awaiter_t *wd)
235
{
249
{
236
    link_t *tmp;
250
    link_t *tmp;
237
    awaiter_t *cur;
251
    awaiter_t *cur;
238
 
252
 
239
    wd->timedout = 0;
253
    wd->timedout = 0;
240
    wd->inlist = 1;
254
    wd->inlist = 1;
241
 
255
 
242
    tmp = timeout_list.next;
256
    tmp = timeout_list.next;
243
    while (tmp != &timeout_list) {
257
    while (tmp != &timeout_list) {
244
        cur = list_get_instance(tmp, awaiter_t, link);
258
        cur = list_get_instance(tmp, awaiter_t, link);
245
        if (tv_gteq(&cur->expires, &wd->expires))
259
        if (tv_gteq(&cur->expires, &wd->expires))
246
            break;
260
            break;
247
        tmp = tmp->next;
261
        tmp = tmp->next;
248
    }
262
    }
249
    list_append(&wd->link, tmp);
263
    list_append(&wd->link, tmp);
250
}
264
}
251
 
265
 
252
/** Try to route a call to an appropriate connection fibril.
266
/** Try to route a call to an appropriate connection fibril.
253
 *
267
 *
-
 
268
 * If the proper connection fibril is found, a message with the call is added to
-
 
269
 * its message queue. If the fibril was not active, it is activated and all
-
 
270
 * timeouts are unregistered.
-
 
271
 *
-
 
272
 * @param callid    Hash of the incoming call.
-
 
273
 * @param call      Data of the incoming call.
-
 
274
 *
-
 
275
 * @return      Zero if the call doesn't match any connection.
-
 
276
 *          One if the call was passed to the respective connection
-
 
277
 *          fibril.
254
 */
278
 */
255
static int route_call(ipc_callid_t callid, ipc_call_t *call)
279
static int route_call(ipc_callid_t callid, ipc_call_t *call)
256
{
280
{
257
    connection_t *conn;
281
    connection_t *conn;
258
    msg_t *msg;
282
    msg_t *msg;
259
    link_t *hlp;
283
    link_t *hlp;
260
    unsigned long key;
284
    unsigned long key;
261
 
285
 
262
    futex_down(&async_futex);
286
    futex_down(&async_futex);
263
 
287
 
264
    key = call->in_phone_hash;
288
    key = call->in_phone_hash;
265
    hlp = hash_table_find(&conn_hash_table, &key);
289
    hlp = hash_table_find(&conn_hash_table, &key);
266
    if (!hlp) {
290
    if (!hlp) {
267
        futex_up(&async_futex);
291
        futex_up(&async_futex);
268
        return 0;
292
        return 0;
269
    }
293
    }
270
    conn = hash_table_get_instance(hlp, connection_t, link);
294
    conn = hash_table_get_instance(hlp, connection_t, link);
271
 
295
 
272
    msg = malloc(sizeof(*msg));
296
    msg = malloc(sizeof(*msg));
273
    msg->callid = callid;
297
    msg->callid = callid;
274
    msg->call = *call;
298
    msg->call = *call;
275
    list_append(&msg->link, &conn->msg_queue);
299
    list_append(&msg->link, &conn->msg_queue);
276
 
300
 
277
    if (IPC_GET_METHOD(*call) == IPC_M_PHONE_HUNGUP)
301
    if (IPC_GET_METHOD(*call) == IPC_M_PHONE_HUNGUP)
278
        conn->close_callid = callid;
302
        conn->close_callid = callid;
279
   
303
   
280
    /* If the call is waiting for event, run it */
304
    /* If the connection fibril is waiting for an event, activate it */
281
    if (!conn->wdata.active) {
305
    if (!conn->wdata.active) {
282
        /* If in timeout list, remove it */
306
        /* If in timeout list, remove it */
283
        if (conn->wdata.inlist) {
307
        if (conn->wdata.inlist) {
284
            conn->wdata.inlist = 0;
308
            conn->wdata.inlist = 0;
285
            list_remove(&conn->wdata.link);
309
            list_remove(&conn->wdata.link);
286
        }
310
        }
287
        conn->wdata.active = 1;
311
        conn->wdata.active = 1;
288
        fibril_add_ready(conn->wdata.fid);
312
        fibril_add_ready(conn->wdata.fid);
289
    }
313
    }
290
 
314
 
291
    futex_up(&async_futex);
315
    futex_up(&async_futex);
292
 
316
 
293
    return 1;
317
    return 1;
294
}
318
}
295
 
319
 
296
/** Return new incoming message for the current (fibril-local) connection.
320
/** Return new incoming message for the current (fibril-local) connection.
297
 *
321
 *
298
 * @param call      Storage where the incoming call data will be stored.
322
 * @param call      Storage where the incoming call data will be stored.
299
 * @param usecs     Timeout in microseconds. Zero denotes no timeout.
323
 * @param usecs     Timeout in microseconds. Zero denotes no timeout.
300
 *
324
 *
301
 * @return      If no timeout was specified, then a hash of the
325
 * @return      If no timeout was specified, then a hash of the
302
 *          incoming call is returned. If a timeout is specified,
326
 *          incoming call is returned. If a timeout is specified,
303
 *          then a hash of the incoming call is returned unless
327
 *          then a hash of the incoming call is returned unless
304
 *          the timeout expires prior to receiving a message. In
328
 *          the timeout expires prior to receiving a message. In
305
 *          that case zero is returned.
329
 *          that case zero is returned.
306
 */
330
 */
307
ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
331
ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
308
{
332
{
309
    msg_t *msg;
333
    msg_t *msg;
310
    ipc_callid_t callid;
334
    ipc_callid_t callid;
311
    connection_t *conn;
335
    connection_t *conn;
312
   
336
   
313
    assert(FIBRIL_connection);
337
    assert(FIBRIL_connection);
314
    /* GCC 4.1.0 coughs on FIBRIL_connection-> dereference,
338
    /* GCC 4.1.0 coughs on FIBRIL_connection-> dereference,
315
     * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
339
     * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
316
     *           I would never expect to find so many errors in
340
     *           I would never expect to find so many errors in
317
     *           compiler *($&$(*&$
341
     *           a compiler *($&$(*&$
318
     */
342
     */
319
    conn = FIBRIL_connection;
343
    conn = FIBRIL_connection;
320
 
344
 
321
    futex_down(&async_futex);
345
    futex_down(&async_futex);
322
 
346
 
323
    if (usecs) {
347
    if (usecs) {
324
        gettimeofday(&conn->wdata.expires, NULL);
348
        gettimeofday(&conn->wdata.expires, NULL);
325
        tv_add(&conn->wdata.expires, usecs);
349
        tv_add(&conn->wdata.expires, usecs);
326
    } else {
350
    } else {
327
        conn->wdata.inlist = 0;
351
        conn->wdata.inlist = 0;
328
    }
352
    }
329
    /* If nothing in queue, wait until something arrives */
353
    /* If nothing in queue, wait until something arrives */
330
    while (list_empty(&conn->msg_queue)) {
354
    while (list_empty(&conn->msg_queue)) {
331
        if (usecs)
355
        if (usecs)
332
            insert_timeout(&conn->wdata);
356
            insert_timeout(&conn->wdata);
333
 
357
 
334
        conn->wdata.active = 0;
358
        conn->wdata.active = 0;
335
        fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
359
        fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
336
        /*
360
        /*
337
         * Futex is up after getting back from async_manager get it
361
         * Futex is up after getting back from async_manager get it
338
         * again.
362
         * again.
339
        */
363
        */
340
        futex_down(&async_futex);
364
        futex_down(&async_futex);
341
        if (usecs && conn->wdata.timedout &&
365
        if (usecs && conn->wdata.timedout &&
342
            list_empty(&conn->msg_queue)) {
366
            list_empty(&conn->msg_queue)) {
343
            /* If we timed out -> exit */
367
            /* If we timed out -> exit */
344
            futex_up(&async_futex);
368
            futex_up(&async_futex);
345
            return 0;
369
            return 0;
346
        }
370
        }
347
    }
371
    }
348
   
372
   
349
    msg = list_get_instance(conn->msg_queue.next, msg_t, link);
373
    msg = list_get_instance(conn->msg_queue.next, msg_t, link);
350
    list_remove(&msg->link);
374
    list_remove(&msg->link);
351
    callid = msg->callid;
375
    callid = msg->callid;
352
    *call = msg->call;
376
    *call = msg->call;
353
    free(msg);
377
    free(msg);
354
   
378
   
355
    futex_up(&async_futex);
379
    futex_up(&async_futex);
356
    return callid;
380
    return callid;
357
}
381
}
358
 
382
 
359
/** Fibril function that gets created on new connection
383
/** Default fibril function that gets called to handle new connection.
360
 *
384
 *
361
 * This function is defined as a weak symbol - to be redefined in user code.
385
 * This function is defined as a weak symbol - to be redefined in user code.
-
 
386
 *
-
 
387
 * @param callid    Hash of the incoming call.
-
 
388
 * @param call      Data of the incoming call.
362
 */
389
 */
363
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)
390
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)
364
{
391
{
365
    ipc_answer_fast(callid, ENOENT, 0, 0);
392
    ipc_answer_fast(callid, ENOENT, 0, 0);
366
}
393
}
-
 
394
 
-
 
395
/** Default fibril function that gets called to handle interrupt notifications.
-
 
396
 *
-
 
397
 * @param callid    Hash of the incoming call.
-
 
398
 * @param call      Data of the incoming call.
-
 
399
 */
367
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
400
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
368
{
401
{
369
}
402
}
370
 
403
 
371
/** Wrapper for client connection fibril.
404
/** Wrapper for client connection fibril.
372
 *
405
 *
373
 * When new connection arrives, a fibril with this implementing function is
406
 * When a new connection arrives, a fibril with this implementing function is
374
 * created. It calls client_connection() and does the final cleanup.
407
 * created. It calls client_connection() and does the final cleanup.
375
 *
408
 *
376
 * @param arg       Connection structure pointer
409
 * @param arg       Connection structure pointer.
377
 *
410
 *
378
 * @return      Always zero.
411
 * @return      Always zero.
379
 */
412
 */
380
static int connection_fibril(void  *arg)
413
static int connection_fibril(void  *arg)
381
{
414
{
382
    unsigned long key;
415
    unsigned long key;
383
    msg_t *msg;
416
    msg_t *msg;
384
    int close_answered = 0;
417
    int close_answered = 0;
385
 
418
 
386
    /* Setup fibril-local connection pointer */
419
    /* Setup fibril-local connection pointer */
387
    FIBRIL_connection = (connection_t *) arg;
420
    FIBRIL_connection = (connection_t *) arg;
388
    FIBRIL_connection->cfibril(FIBRIL_connection->callid,
421
    FIBRIL_connection->cfibril(FIBRIL_connection->callid,
389
        &FIBRIL_connection->call);
422
        &FIBRIL_connection->call);
390
   
423
   
391
    /* Remove myself from connection hash table */
424
    /* Remove myself from the connection hash table */
392
    futex_down(&async_futex);
425
    futex_down(&async_futex);
393
    key = FIBRIL_connection->in_phone_hash;
426
    key = FIBRIL_connection->in_phone_hash;
394
    hash_table_remove(&conn_hash_table, &key, 1);
427
    hash_table_remove(&conn_hash_table, &key, 1);
395
    futex_up(&async_futex);
428
    futex_up(&async_futex);
396
   
429
   
397
    /* Answer all remaining messages with ehangup */
430
    /* Answer all remaining messages with EHANGUP */
398
    while (!list_empty(&FIBRIL_connection->msg_queue)) {
431
    while (!list_empty(&FIBRIL_connection->msg_queue)) {
399
        msg = list_get_instance(FIBRIL_connection->msg_queue.next,
432
        msg = list_get_instance(FIBRIL_connection->msg_queue.next,
400
            msg_t, link);
433
            msg_t, link);
401
        list_remove(&msg->link);
434
        list_remove(&msg->link);
402
        if (msg->callid == FIBRIL_connection->close_callid)
435
        if (msg->callid == FIBRIL_connection->close_callid)
403
            close_answered = 1;
436
            close_answered = 1;
404
        ipc_answer_fast(msg->callid, EHANGUP, 0, 0);
437
        ipc_answer_fast(msg->callid, EHANGUP, 0, 0);
405
        free(msg);
438
        free(msg);
406
    }
439
    }
407
    if (FIBRIL_connection->close_callid)
440
    if (FIBRIL_connection->close_callid)
408
        ipc_answer_fast(FIBRIL_connection->close_callid, 0, 0, 0);
441
        ipc_answer_fast(FIBRIL_connection->close_callid, 0, 0, 0);
409
   
442
   
410
    return 0;
443
    return 0;
411
}
444
}
412
 
445
 
413
/** Create a new fibril for a new connection.
446
/** Create a new fibril for a new connection.
414
 *
447
 *
415
 * Creates new fibril for connection, fills in connection structures and inserts
448
 * Creates new fibril for connection, fills in connection structures and inserts
416
 * it into the hash table, so that later we can easily do routing of messages to
449
 * it into the hash table, so that later we can easily do routing of messages to
417
 * particular fibrils.
450
 * particular fibrils.
418
 *
451
 *
419
 * @param in_phone_hash Identification of the incoming connection
452
 * @param in_phone_hash Identification of the incoming connection.
420
 * @param callid    Callid of the IPC_M_CONNECT_ME_TO packet
453
 * @param callid    Hash of the opening IPC_M_CONNECT_ME_TO call.
421
 * @param call      Call data of the opening packet
454
 * @param call      Call data of the opening call.
422
 * @param cfibril   Fibril function that should be called upon
455
 * @param cfibril   Fibril function that should be called upon opening the
423
 *                  opening the connection
456
 *          connection.
-
 
457
 *
424
 * @return      New fibril id.
458
 * @return      New fibril id or NULL on failure.
425
 */
459
 */
426
fid_t async_new_connection(ipcarg_t in_phone_hash, ipc_callid_t callid,
460
fid_t async_new_connection(ipcarg_t in_phone_hash, ipc_callid_t callid,
427
    ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *))
461
    ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *))
428
{
462
{
429
    connection_t *conn;
463
    connection_t *conn;
430
    unsigned long key;
464
    unsigned long key;
431
 
465
 
432
    conn = malloc(sizeof(*conn));
466
    conn = malloc(sizeof(*conn));
433
    if (!conn) {
467
    if (!conn) {
434
        ipc_answer_fast(callid, ENOMEM, 0, 0);
468
        ipc_answer_fast(callid, ENOMEM, 0, 0);
435
        return NULL;
469
        return NULL;
436
    }
470
    }
437
    conn->in_phone_hash = in_phone_hash;
471
    conn->in_phone_hash = in_phone_hash;
438
    list_initialize(&conn->msg_queue);
472
    list_initialize(&conn->msg_queue);
439
    conn->callid = callid;
473
    conn->callid = callid;
440
    conn->close_callid = 0;
474
    conn->close_callid = 0;
441
    if (call)
475
    if (call)
442
        conn->call = *call;
476
        conn->call = *call;
443
    conn->wdata.active = 1; /* We will activate it asap */
477
    conn->wdata.active = 1; /* We will activate the fibril ASAP */
444
    conn->cfibril = cfibril;
478
    conn->cfibril = cfibril;
445
 
479
 
446
    conn->wdata.fid = fibril_create(connection_fibril, conn);
480
    conn->wdata.fid = fibril_create(connection_fibril, conn);
447
    if (!conn->wdata.fid) {
481
    if (!conn->wdata.fid) {
448
        free(conn);
482
        free(conn);
449
        ipc_answer_fast(callid, ENOMEM, 0, 0);
483
        ipc_answer_fast(callid, ENOMEM, 0, 0);
450
        return NULL;
484
        return NULL;
451
    }
485
    }
452
    /* Add connection to hash table */
486
    /* Add connection to the connection hash table */
453
    key = conn->in_phone_hash;
487
    key = conn->in_phone_hash;
454
    futex_down(&async_futex);
488
    futex_down(&async_futex);
455
    hash_table_insert(&conn_hash_table, &key, &conn->link);
489
    hash_table_insert(&conn_hash_table, &key, &conn->link);
456
    futex_up(&async_futex);
490
    futex_up(&async_futex);
457
 
491
 
458
    fibril_add_ready(conn->wdata.fid);
492
    fibril_add_ready(conn->wdata.fid);
459
 
493
 
460
    return conn->wdata.fid;
494
    return conn->wdata.fid;
461
}
495
}
462
 
496
 
463
/** Handle a call that was received. */
497
/** Handle a call that was received.
-
 
498
 *
-
 
499
 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
-
 
500
 * Otherwise the call is routed to its connection fibril.
-
 
501
 *
-
 
502
 * @param callid    Hash of the incoming call.
-
 
503
 * @param call      Data of the incoming call.
-
 
504
 */
464
static void handle_call(ipc_callid_t callid, ipc_call_t *call)
505
static void handle_call(ipc_callid_t callid, ipc_call_t *call)
465
{
506
{
466
    /* Unrouted call - do some default behaviour */
507
    /* Unrouted call - do some default behaviour */
467
    if ((callid & IPC_CALLID_NOTIFICATION)) {
508
    if ((callid & IPC_CALLID_NOTIFICATION)) {
468
        in_interrupt_handler = 1;
509
        in_interrupt_handler = 1;
469
        (*interrupt_received)(callid,call);
510
        (*interrupt_received)(callid,call);
470
        in_interrupt_handler = 0;
511
        in_interrupt_handler = 0;
471
        return;
512
        return;
472
    }      
513
    }      
473
 
514
 
474
    switch (IPC_GET_METHOD(*call)) {
515
    switch (IPC_GET_METHOD(*call)) {
475
    case IPC_M_CONNECT_ME_TO:
516
    case IPC_M_CONNECT_ME_TO:
476
        /* Open new connection with fibril etc. */
517
        /* Open new connection with fibril etc. */
477
        async_new_connection(IPC_GET_ARG3(*call), callid, call,
518
        async_new_connection(IPC_GET_ARG3(*call), callid, call,
478
            client_connection);
519
            client_connection);
479
        return;
520
        return;
480
    }
521
    }
481
 
522
 
482
    /* Try to route call through connection tables */
523
    /* Try to route the call through the connection hash table */
483
    if (route_call(callid, call))
524
    if (route_call(callid, call))
484
        return;
525
        return;
485
 
526
 
486
    /* Unknown call from unknown phone - hang it up */
527
    /* Unknown call from unknown phone - hang it up */
487
    ipc_answer_fast(callid, EHANGUP, 0, 0);
528
    ipc_answer_fast(callid, EHANGUP, 0, 0);
488
}
529
}
489
 
530
 
490
/** Fire all timeouts that expired. */
531
/** Fire all timeouts that expired. */
491
static void handle_expired_timeouts(void)
532
static void handle_expired_timeouts(void)
492
{
533
{
493
    struct timeval tv;
534
    struct timeval tv;
494
    awaiter_t *waiter;
535
    awaiter_t *waiter;
495
    link_t *cur;
536
    link_t *cur;
496
 
537
 
497
    gettimeofday(&tv,NULL);
538
    gettimeofday(&tv, NULL);
498
    futex_down(&async_futex);
539
    futex_down(&async_futex);
499
 
540
 
500
    cur = timeout_list.next;
541
    cur = timeout_list.next;
501
    while (cur != &timeout_list) {
542
    while (cur != &timeout_list) {
502
        waiter = list_get_instance(cur, awaiter_t, link);
543
        waiter = list_get_instance(cur, awaiter_t, link);
503
        if (tv_gt(&waiter->expires, &tv))
544
        if (tv_gt(&waiter->expires, &tv))
504
            break;
545
            break;
505
        cur = cur->next;
546
        cur = cur->next;
506
        list_remove(&waiter->link);
547
        list_remove(&waiter->link);
507
        waiter->inlist = 0;
548
        waiter->inlist = 0;
508
        waiter->timedout = 1;
549
        waiter->timedout = 1;
-
 
550
        /*
509
        /* Redundant condition? The fibril should not
551
         * Redundant condition?
510
         * be active when it gets here.
552
         * The fibril should not be active when it gets here.
511
         */
553
         */
512
        if (!waiter->active) {
554
        if (!waiter->active) {
513
            waiter->active = 1;
555
            waiter->active = 1;
514
            fibril_add_ready(waiter->fid);
556
            fibril_add_ready(waiter->fid);
515
        }
557
        }
516
    }
558
    }
517
 
559
 
518
    futex_up(&async_futex);
560
    futex_up(&async_futex);
519
}
561
}
520
 
562
 
521
/** Endless loop dispatching incoming calls and answers */
563
/** Endless loop dispatching incoming calls and answers.
-
 
564
 *
-
 
565
 * @return      Never returns.
-
 
566
 */
522
static int async_manager_worker(void)
567
static int async_manager_worker(void)
523
{
568
{
524
    ipc_call_t call;
569
    ipc_call_t call;
525
    ipc_callid_t callid;
570
    ipc_callid_t callid;
526
    int timeout;
571
    int timeout;
527
    awaiter_t *waiter;
572
    awaiter_t *waiter;
528
    struct timeval tv;
573
    struct timeval tv;
529
 
574
 
530
    while (1) {
575
    while (1) {
531
        if (fibril_schedule_next_adv(FIBRIL_FROM_MANAGER)) {
576
        if (fibril_schedule_next_adv(FIBRIL_FROM_MANAGER)) {
532
            futex_up(&async_futex);
577
            futex_up(&async_futex);
-
 
578
            /*
533
            /* async_futex is always held
579
             * async_futex is always held when entering a manager
534
             * when entering manager fibril
580
             * fibril.
535
             */
581
             */
536
            continue;
582
            continue;
537
        }
583
        }
538
        futex_down(&async_futex);
584
        futex_down(&async_futex);
539
        if (!list_empty(&timeout_list)) {
585
        if (!list_empty(&timeout_list)) {
540
            waiter = list_get_instance(timeout_list.next, awaiter_t,
586
            waiter = list_get_instance(timeout_list.next, awaiter_t,
541
                link);
587
                link);
542
            gettimeofday(&tv, NULL);
588
            gettimeofday(&tv, NULL);
543
            if (tv_gteq(&tv, &waiter->expires)) {
589
            if (tv_gteq(&tv, &waiter->expires)) {
544
                futex_up(&async_futex);
590
                futex_up(&async_futex);
545
                handle_expired_timeouts();
591
                handle_expired_timeouts();
546
                continue;
592
                continue;
547
            } else
593
            } else
548
                timeout = tv_sub(&waiter->expires, &tv);
594
                timeout = tv_sub(&waiter->expires, &tv);
549
        } else
595
        } else
550
            timeout = SYNCH_NO_TIMEOUT;
596
            timeout = SYNCH_NO_TIMEOUT;
551
        futex_up(&async_futex);
597
        futex_up(&async_futex);
552
 
598
 
553
        callid = ipc_wait_cycle(&call, timeout, SYNCH_FLAGS_NONE);
599
        callid = ipc_wait_cycle(&call, timeout, SYNCH_FLAGS_NONE);
554
 
600
 
555
        if (!callid) {
601
        if (!callid) {
556
            handle_expired_timeouts();
602
            handle_expired_timeouts();
557
            continue;
603
            continue;
558
        }
604
        }
559
 
605
 
560
        if (callid & IPC_CALLID_ANSWERED) {
606
        if (callid & IPC_CALLID_ANSWERED) {
561
            continue;
607
            continue;
562
        }
608
        }
563
 
609
 
564
        handle_call(callid, &call);
610
        handle_call(callid, &call);
565
    }
611
    }
566
   
612
   
567
    return 0;
613
    return 0;
568
}
614
}
569
 
615
 
570
/** Function to start async_manager as a standalone fibril.
616
/** Function to start async_manager as a standalone fibril.
571
 *
617
 *
572
 * When more kernel threads are used, one async manager should
618
 * When more kernel threads are used, one async manager should exist per thread.
-
 
619
 *
-
 
620
 * @param arg       Unused.
-
 
621
 *
573
 * exist per thread.
622
 * @return      Never returns.
574
 */
623
 */
575
static int async_manager_fibril(void *arg)
624
static int async_manager_fibril(void *arg)
576
{
625
{
577
    futex_up(&async_futex);
626
    futex_up(&async_futex);
-
 
627
    /*
578
    /* async_futex is always locked when entering
628
     * async_futex is always locked when entering manager
579
     * manager */
629
     */
580
    async_manager_worker();
630
    async_manager_worker();
581
   
631
   
582
    return 0;
632
    return 0;
583
}
633
}
584
 
634
 
585
/** Add one manager to manager list */
635
/** Add one manager to manager list. */
586
void async_create_manager(void)
636
void async_create_manager(void)
587
{
637
{
588
    fid_t fid;
638
    fid_t fid;
589
 
639
 
590
    fid = fibril_create(async_manager_fibril, NULL);
640
    fid = fibril_create(async_manager_fibril, NULL);
591
    fibril_add_manager(fid);
641
    fibril_add_manager(fid);
592
}
642
}
593
 
643
 
594
/** Remove one manager from manager list */
644
/** Remove one manager from manager list */
595
void async_destroy_manager(void)
645
void async_destroy_manager(void)
596
{
646
{
597
    fibril_remove_manager();
647
    fibril_remove_manager();
598
}
648
}
599
 
649
 
600
/** Initialize internal structures needed for async manager */
650
/** Initialize the async framework.
-
 
651
 *
-
 
652
 * @return      Zero on success or an error code.
-
 
653
 */
601
int _async_init(void)
654
int _async_init(void)
602
{
655
{
603
    if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
656
    if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
604
        &conn_hash_table_ops)) {
657
        &conn_hash_table_ops)) {
605
        printf("%s: cannot create hash table\n", "async");
658
        printf("%s: cannot create hash table\n", "async");
606
        return ENOMEM;
659
        return ENOMEM;
607
    }
660
    }
608
   
661
   
609
    return 0;
662
    return 0;
610
}
663
}
611
 
664
 
-
 
665
/** Reply received callback.
-
 
666
 *
-
 
667
 * This function is called whenever a reply for an asynchronous message sent out
612
/** IPC handler for messages in async framework
668
 * by the asynchronous framework is received.
-
 
669
 *
-
 
670
 * Notify the fibril which is waiting for this message that it has arrived.
613
 *
671
 *
614
 * Notify the fibril which is waiting for this message, that it arrived
672
 * @param private   Pointer to the asynchronous message record.
-
 
673
 * @param retval    Value returned in the answer.
-
 
674
 * @param data      Call data of the answer.
615
 */
675
 */
616
static void reply_received(void *private, int retval, ipc_call_t *data)
676
static void reply_received(void *private, int retval, ipc_call_t *data)
617
{
677
{
618
    amsg_t *msg = (amsg_t *) private;
678
    amsg_t *msg = (amsg_t *) private;
619
 
679
 
620
    msg->retval = retval;
680
    msg->retval = retval;
621
 
681
 
622
    futex_down(&async_futex);
682
    futex_down(&async_futex);
623
    /* Copy data after futex_down, just in case the
683
    /* Copy data after futex_down, just in case the call was detached */
624
     * call was detached
-
 
625
     */
-
 
626
    if (msg->dataptr)
684
    if (msg->dataptr)
627
        *msg->dataptr = *data;
685
        *msg->dataptr = *data;
628
 
686
 
629
    write_barrier();
687
    write_barrier();
630
    /* Remove message from timeout list */
688
    /* Remove message from timeout list */
631
    if (msg->wdata.inlist)
689
    if (msg->wdata.inlist)
632
        list_remove(&msg->wdata.link);
690
        list_remove(&msg->wdata.link);
633
    msg->done = 1;
691
    msg->done = 1;
634
    if (! msg->wdata.active) {
692
    if (!msg->wdata.active) {
635
        msg->wdata.active = 1;
693
        msg->wdata.active = 1;
636
        fibril_add_ready(msg->wdata.fid);
694
        fibril_add_ready(msg->wdata.fid);
637
    }
695
    }
638
    futex_up(&async_futex);
696
    futex_up(&async_futex);
639
}
697
}
640
 
698
 
641
/** Send message and return id of the sent message
699
/** Send message and return id of the sent message.
-
 
700
 *
-
 
701
 * The return value can be used as input for async_wait() to wait for
-
 
702
 * completion.
-
 
703
 *
-
 
704
 * @param phoneid   Handle of the phone that will be used for the send.
-
 
705
 * @param method    Service-defined method.
-
 
706
 * @param arg1      Service-defined payload argument.
-
 
707
 * @param arg2      Service-defined payload argument.
-
 
708
 * @param dataptr   If non-NULL, storage where the reply data will be
-
 
709
 *          stored.
642
 *
710
 *
643
 * The return value can be used as input for async_wait() to wait
711
 * @return      Hash of the sent message.
644
 * for completion.
-
 
645
 */
712
 */
646
aid_t async_send_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
713
aid_t async_send_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
647
    ipc_call_t *dataptr)
714
    ipc_call_t *dataptr)
648
{
715
{
649
    amsg_t *msg;
716
    amsg_t *msg;
650
 
717
 
651
    if (in_interrupt_handler) {
718
    if (in_interrupt_handler) {
652
        printf("Cannot send asynchronous request in interrupt "
719
        printf("Cannot send asynchronous request in interrupt "
653
            "handler.\n");
720
            "handler.\n");
654
        _exit(1);
721
        _exit(1);
655
    }
722
    }
656
 
723
 
657
    msg = malloc(sizeof(*msg));
724
    msg = malloc(sizeof(*msg));
658
    msg->done = 0;
725
    msg->done = 0;
659
    msg->dataptr = dataptr;
726
    msg->dataptr = dataptr;
660
 
727
 
661
    msg->wdata.active = 1; /* We may sleep in next method, but it
728
    /* We may sleep in the next method, but it will use its own mechanism */
662
                * will use it's own mechanism */
729
    msg->wdata.active = 1;
-
 
730
               
663
    ipc_call_async_2(phoneid, method, arg1, arg2, msg, reply_received, 1);
731
    ipc_call_async_2(phoneid, method, arg1, arg2, msg, reply_received, 1);
664
 
732
 
665
    return (aid_t) msg;
733
    return (aid_t) msg;
666
}
734
}
667
 
735
 
668
/** Send message and return id of the sent message
736
/** Send message and return id of the sent message
669
 *
737
 *
670
 * The return value can be used as input for async_wait() to wait
738
 * The return value can be used as input for async_wait() to wait for
671
 * for completion.
739
 * completion.
-
 
740
 *
-
 
741
 * @param phoneid   Handle of the phone that will be used for the send.
-
 
742
 * @param method    Service-defined method.
-
 
743
 * @param arg1      Service-defined payload argument.
-
 
744
 * @param arg2      Service-defined payload argument.
-
 
745
 * @param arg3      Service-defined payload argument.
-
 
746
 * @param dataptr   If non-NULL, storage where the reply data will be
-
 
747
 *          stored.
-
 
748
 *
-
 
749
 * @return      Hash of the sent message.
672
 */
750
 */
673
aid_t async_send_3(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
751
aid_t async_send_3(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
674
    ipcarg_t arg3, ipc_call_t *dataptr)
752
    ipcarg_t arg3, ipc_call_t *dataptr)
675
{
753
{
676
    amsg_t *msg;
754
    amsg_t *msg;
677
 
755
 
678
    if (in_interrupt_handler) {
756
    if (in_interrupt_handler) {
679
        printf("Cannot send asynchronous request in interrupt handler.\n");
757
        printf("Cannot send asynchronous request in interrupt "
-
 
758
            "handler.\n");
680
        _exit(1);
759
        _exit(1);
681
    }
760
    }
682
 
761
 
683
    msg = malloc(sizeof(*msg));
762
    msg = malloc(sizeof(*msg));
684
    msg->done = 0;
763
    msg->done = 0;
685
    msg->dataptr = dataptr;
764
    msg->dataptr = dataptr;
686
 
765
 
687
    msg->wdata.active = 1; /* We may sleep in next method, but it
766
    /* We may sleep in next method, but it will use its own mechanism */
688
                * will use it's own mechanism */
767
    msg->wdata.active = 1;
-
 
768
               
689
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, msg, reply_received,
769
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, msg, reply_received,
690
        1);
770
        1);
691
 
771
 
692
    return (aid_t) msg;
772
    return (aid_t) msg;
693
}
773
}
694
 
774
 
695
/** Wait for a message sent by async framework
775
/** Wait for a message sent by the async framework.
696
 *
776
 *
697
 * @param amsgid    Message ID to wait for
777
 * @param amsgid    Hash of the message to wait for.
698
 * @param retval    Pointer to variable where will be stored retval of the
778
 * @param retval    Pointer to storage where the retval of the answer will
699
 *          answered message. If NULL, it is ignored.
779
 *          be stored.
700
 */
780
 */
701
void async_wait_for(aid_t amsgid, ipcarg_t *retval)
781
void async_wait_for(aid_t amsgid, ipcarg_t *retval)
702
{
782
{
703
    amsg_t *msg = (amsg_t *) amsgid;
783
    amsg_t *msg = (amsg_t *) amsgid;
704
 
784
 
705
    futex_down(&async_futex);
785
    futex_down(&async_futex);
706
    if (msg->done) {
786
    if (msg->done) {
707
        futex_up(&async_futex);
787
        futex_up(&async_futex);
708
        goto done;
788
        goto done;
709
    }
789
    }
710
 
790
 
711
    msg->wdata.fid = fibril_get_id();
791
    msg->wdata.fid = fibril_get_id();
712
    msg->wdata.active = 0;
792
    msg->wdata.active = 0;
713
    msg->wdata.inlist = 0;
793
    msg->wdata.inlist = 0;
714
    /* Leave locked async_futex when entering this function */
794
    /* Leave the async_futex locked when entering this function */
715
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
795
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
716
    /* futex is up automatically after fibril_schedule_next...*/
796
    /* futex is up automatically after fibril_schedule_next...*/
717
done:
797
done:
718
    if (retval)
798
    if (retval)
719
        *retval = msg->retval;
799
        *retval = msg->retval;
720
    free(msg);
800
    free(msg);
721
}
801
}
722
 
802
 
723
/** Wait for a message sent by async framework with timeout
803
/** Wait for a message sent by the async framework, timeout variant.
724
 *
804
 *
725
 * @param amsgid Message ID to wait for
805
 * @param amsgid    Hash of the message to wait for.
726
 * @param retval Pointer to variable where will be stored retval
806
 * @param retval    Pointer to storage where the retval of the answer will
727
 *               of the answered message. If NULL, it is ignored.
807
 *          be stored.
728
 * @param timeout Timeout in usecs
808
 * @param timeout   Timeout in microseconds.
729
 * @return 0 on success, ETIMEOUT if timeout expired
-
 
730
 *
809
 *
-
 
810
 * @return      Zero on success, ETIMEOUT if the timeout has expired.
731
 */
811
 */
732
int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout)
812
int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout)
733
{
813
{
734
    amsg_t *msg = (amsg_t *) amsgid;
814
    amsg_t *msg = (amsg_t *) amsgid;
735
 
815
 
736
    /* TODO: Let it go through the event read at least once */
816
    /* TODO: Let it go through the event read at least once */
737
    if (timeout < 0)
817
    if (timeout < 0)
738
        return ETIMEOUT;
818
        return ETIMEOUT;
739
 
819
 
740
    futex_down(&async_futex);
820
    futex_down(&async_futex);
741
    if (msg->done) {
821
    if (msg->done) {
742
        futex_up(&async_futex);
822
        futex_up(&async_futex);
743
        goto done;
823
        goto done;
744
    }
824
    }
745
 
825
 
746
    gettimeofday(&msg->wdata.expires, NULL);
826
    gettimeofday(&msg->wdata.expires, NULL);
747
    tv_add(&msg->wdata.expires, timeout);
827
    tv_add(&msg->wdata.expires, timeout);
748
 
828
 
749
    msg->wdata.fid = fibril_get_id();
829
    msg->wdata.fid = fibril_get_id();
750
    msg->wdata.active = 0;
830
    msg->wdata.active = 0;
751
    insert_timeout(&msg->wdata);
831
    insert_timeout(&msg->wdata);
752
 
832
 
753
    /* Leave locked async_futex when entering this function */
833
    /* Leave the async_futex locked when entering this function */
754
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
834
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
755
    /* futex is up automatically after fibril_schedule_next...*/
835
    /* futex is up automatically after fibril_schedule_next...*/
756
 
836
 
757
    if (!msg->done)
837
    if (!msg->done)
758
        return ETIMEOUT;
838
        return ETIMEOUT;
759
 
839
 
760
done:
840
done:
761
    if (retval)
841
    if (retval)
762
        *retval = msg->retval;
842
        *retval = msg->retval;
763
    free(msg);
843
    free(msg);
764
 
844
 
765
    return 0;
845
    return 0;
766
}
846
}
767
 
847
 
-
 
848
/** Wait for specified time.
-
 
849
 *
768
/** Wait specified time, but in the meantime handle incoming events
850
 * The current fibril is suspended but the thread continues to execute.
769
 *
851
 *
770
 * @param timeout Time in microseconds to wait
852
 * @param timeout   Duration of the wait in microseconds.
771
 */
853
 */
772
void async_usleep(suseconds_t timeout)
854
void async_usleep(suseconds_t timeout)
773
{
855
{
774
    amsg_t *msg;
856
    amsg_t *msg;
775
   
857
   
776
    if (in_interrupt_handler) {
858
    if (in_interrupt_handler) {
777
        printf("Cannot call async_usleep in interrupt handler.\n");
859
        printf("Cannot call async_usleep in interrupt handler.\n");
778
        _exit(1);
860
        _exit(1);
779
    }
861
    }
780
 
862
 
781
    msg = malloc(sizeof(*msg));
863
    msg = malloc(sizeof(*msg));
782
    if (!msg)
864
    if (!msg)
783
        return;
865
        return;
784
 
866
 
785
    msg->wdata.fid = fibril_get_id();
867
    msg->wdata.fid = fibril_get_id();
786
    msg->wdata.active = 0;
868
    msg->wdata.active = 0;
787
 
869
 
788
    gettimeofday(&msg->wdata.expires, NULL);
870
    gettimeofday(&msg->wdata.expires, NULL);
789
    tv_add(&msg->wdata.expires, timeout);
871
    tv_add(&msg->wdata.expires, timeout);
790
 
872
 
791
    futex_down(&async_futex);
873
    futex_down(&async_futex);
792
    insert_timeout(&msg->wdata);
874
    insert_timeout(&msg->wdata);
793
    /* Leave locked the async_futex when entering this function */
875
    /* Leave the async_futex locked when entering this function */
794
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
876
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
795
    /* futex is up automatically after fibril_schedule_next_adv()...*/
877
    /* futex is up automatically after fibril_schedule_next_adv()...*/
796
    free(msg);
878
    free(msg);
797
}
879
}
798
 
880
 
799
/** Set function that is called when IPC_M_CONNECT_ME_TO is received.
881
/** Setter for client_connection function pointer.
800
 *
882
 *
801
 * @param conn Function that will form a new fibril.
883
 * @param conn      Function that will implement a new connection fibril.
802
 */
884
 */
803
void async_set_client_connection(async_client_conn_t conn)
885
void async_set_client_connection(async_client_conn_t conn)
804
{
886
{
805
    client_connection = conn;
887
    client_connection = conn;
806
}
888
}
-
 
889
 
-
 
890
/** Setter for interrupt_received function pointer.
-
 
891
 *
-
 
892
 * @param conn      Function that will implement a new interrupt
-
 
893
 *          notification fibril.
-
 
894
 */
807
void async_set_interrupt_received(async_client_conn_t conn)
895
void async_set_interrupt_received(async_client_conn_t conn)
808
{
896
{
809
    interrupt_received = conn;
897
    interrupt_received = conn;
810
}
898
}
811
 
899
 
812
/* Primitive functions for simple communication */
900
/* Primitive functions for simple communication */
813
void async_msg_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
901
void async_msg_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
814
         ipcarg_t arg2, ipcarg_t arg3)
902
         ipcarg_t arg2, ipcarg_t arg3)
815
{
903
{
816
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, NULL, NULL,
904
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, NULL, NULL,
817
        !in_interrupt_handler);
905
        !in_interrupt_handler);
818
}
906
}
819
 
907
 
820
void async_msg_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2)
908
void async_msg_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2)
821
{
909
{
822
    ipc_call_async_2(phoneid, method, arg1, arg2, NULL, NULL,
910
    ipc_call_async_2(phoneid, method, arg1, arg2, NULL, NULL,
823
        !in_interrupt_handler);
911
        !in_interrupt_handler);
824
}
912
}
825
 
913
 
826
/** @}
914
/** @}
827
 */
915
 */
-
 
916
 
828
 
917