Subversion Repositories HelenOS

Rev

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

Rev 2490 Rev 2492
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
 * my_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
    /** Identification of and link to the waiting fibril. */
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
/**
137
/**
138
 * Structures of this type are used to group information about a call and a
138
 * Structures of this type are used to group information about a call and a
139
 * message queue link.
139
 * message queue link.
140
 */
140
 */
141
typedef struct {
141
typedef struct {
142
    link_t link;
142
    link_t link;
143
    ipc_callid_t callid;
143
    ipc_callid_t callid;
144
    ipc_call_t call;
144
    ipc_call_t call;
145
} msg_t;
145
} msg_t;
146
 
146
 
147
typedef struct {
147
typedef struct {
148
    awaiter_t wdata;
148
    awaiter_t wdata;
149
 
149
 
150
    /** Hash table link. */
150
    /** Hash table link. */
151
    link_t link;
151
    link_t link;
152
 
152
 
153
    /** Incoming phone hash. */
153
    /** Incoming phone hash. */
154
    ipcarg_t in_phone_hash;    
154
    ipcarg_t in_phone_hash;    
155
 
155
 
156
    /** Messages that should be delivered to this fibril. */
156
    /** Messages that should be delivered to this fibril. */
157
    link_t msg_queue;      
157
    link_t msg_queue;      
158
                     
158
                     
159
    /** Identification of the opening call. */
159
    /** Identification of the opening call. */
160
    ipc_callid_t callid;
160
    ipc_callid_t callid;
161
    /** Call data of the opening call. */
161
    /** Call data of the opening call. */
162
    ipc_call_t call;
162
    ipc_call_t call;
163
 
163
 
164
    /** Identification of the closing call. */
164
    /** Identification of the closing call. */
165
    ipc_callid_t close_callid;
165
    ipc_callid_t close_callid;
166
 
166
 
167
    /** Fibril function that will be used to handle the connection. */
167
    /** Fibril function that will be used to handle the connection. */
168
    void (*cfibril)(ipc_callid_t, ipc_call_t *);
168
    void (*cfibril)(ipc_callid_t, ipc_call_t *);
169
} connection_t;
169
} connection_t;
170
 
170
 
171
/** Identifier of the incoming connection handled by the current fibril. */
171
/** Identifier of the incoming connection handled by the current fibril. */
172
__thread connection_t *FIBRIL_connection;
172
__thread connection_t *FIBRIL_connection;
173
 
173
 
174
/**
174
/**
175
 * 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
176
 * disabled.
176
 * disabled.
177
 */
177
 */
178
__thread int in_interrupt_handler;
178
__thread int in_interrupt_handler;
179
 
179
 
180
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);
181
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
 
182
 
183
/**
183
/**
184
 * Pointer to a fibril function that will be used to handle connections.
184
 * Pointer to a fibril function that will be used to handle connections.
185
 */
185
 */
186
static async_client_conn_t client_connection = default_client_connection;
186
static async_client_conn_t client_connection = default_client_connection;
187
/**
187
/**
188
 * Pointer to a fibril function that will be used to handle interrupt
188
 * Pointer to a fibril function that will be used to handle interrupt
189
 * notifications.
189
 * notifications.
190
 */
190
 */
191
static async_client_conn_t interrupt_received = default_interrupt_received;
191
static async_client_conn_t interrupt_received = default_interrupt_received;
192
 
192
 
193
#define CONN_HASH_TABLE_CHAINS  32
193
#define CONN_HASH_TABLE_CHAINS  32
194
 
194
 
195
/** 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.
196
 *
196
 *
197
 * @param key       Pointer to source phone hash.
197
 * @param key       Pointer to source phone hash.
198
 *
198
 *
199
 * @return      Index into the connection hash table.
199
 * @return      Index into the connection hash table.
200
 */
200
 */
201
static hash_index_t conn_hash(unsigned long *key)
201
static hash_index_t conn_hash(unsigned long *key)
202
{
202
{
203
    assert(key);
203
    assert(key);
204
    return ((*key) >> 4) % CONN_HASH_TABLE_CHAINS;
204
    return ((*key) >> 4) % CONN_HASH_TABLE_CHAINS;
205
}
205
}
206
 
206
 
207
/** Compare hash table item with a key.
207
/** Compare hash table item with a key.
208
 *
208
 *
209
 * @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.
210
 * @param keys      Expected 1 but ignored.
210
 * @param keys      Expected 1 but ignored.
211
 * @param item      Connection hash table item.
211
 * @param item      Connection hash table item.
212
 *
212
 *
213
 * @return      True on match, false otherwise.
213
 * @return      True on match, false otherwise.
214
 */
214
 */
215
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)
216
{
216
{
217
    connection_t *hs;
217
    connection_t *hs;
218
 
218
 
219
    hs = hash_table_get_instance(item, connection_t, link);
219
    hs = hash_table_get_instance(item, connection_t, link);
220
   
220
   
221
    return key[0] == hs->in_phone_hash;
221
    return key[0] == hs->in_phone_hash;
222
}
222
}
223
 
223
 
224
/** Connection hash table removal callback function.
224
/** Connection hash table removal callback function.
225
 *
225
 *
226
 * 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
227
 * hash table.
227
 * hash table.
228
 *
228
 *
229
 * @param item      Connection hash table item being removed.
229
 * @param item      Connection hash table item being removed.
230
 */
230
 */
231
static void conn_remove(link_t *item)
231
static void conn_remove(link_t *item)
232
{
232
{
233
    free(hash_table_get_instance(item, connection_t, link));
233
    free(hash_table_get_instance(item, connection_t, link));
234
}
234
}
235
 
235
 
236
 
236
 
237
/** Operations for the connection hash table. */
237
/** Operations for the connection hash table. */
238
static hash_table_operations_t conn_hash_table_ops = {
238
static hash_table_operations_t conn_hash_table_ops = {
239
    .hash = conn_hash,
239
    .hash = conn_hash,
240
    .compare = conn_compare,
240
    .compare = conn_compare,
241
    .remove_callback = conn_remove
241
    .remove_callback = conn_remove
242
};
242
};
243
 
243
 
244
/** Sort in current fibril's timeout request.
244
/** Sort in current fibril's timeout request.
245
 *
245
 *
246
 * @param wd        Wait data of the current fibril.
246
 * @param wd        Wait data of the current fibril.
247
 */
247
 */
248
static void insert_timeout(awaiter_t *wd)
248
static void insert_timeout(awaiter_t *wd)
249
{
249
{
250
    link_t *tmp;
250
    link_t *tmp;
251
    awaiter_t *cur;
251
    awaiter_t *cur;
252
 
252
 
253
    wd->timedout = 0;
253
    wd->timedout = 0;
254
    wd->inlist = 1;
254
    wd->inlist = 1;
255
 
255
 
256
    tmp = timeout_list.next;
256
    tmp = timeout_list.next;
257
    while (tmp != &timeout_list) {
257
    while (tmp != &timeout_list) {
258
        cur = list_get_instance(tmp, awaiter_t, link);
258
        cur = list_get_instance(tmp, awaiter_t, link);
259
        if (tv_gteq(&cur->expires, &wd->expires))
259
        if (tv_gteq(&cur->expires, &wd->expires))
260
            break;
260
            break;
261
        tmp = tmp->next;
261
        tmp = tmp->next;
262
    }
262
    }
263
    list_append(&wd->link, tmp);
263
    list_append(&wd->link, tmp);
264
}
264
}
265
 
265
 
266
/** Try to route a call to an appropriate connection fibril.
266
/** Try to route a call to an appropriate connection fibril.
267
 *
267
 *
268
 * If the proper connection fibril is found, a message with the call is added to
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
269
 * its message queue. If the fibril was not active, it is activated and all
270
 * timeouts are unregistered.
270
 * timeouts are unregistered.
271
 *
271
 *
272
 * @param callid    Hash of the incoming call.
272
 * @param callid    Hash of the incoming call.
273
 * @param call      Data of the incoming call.
273
 * @param call      Data of the incoming call.
274
 *
274
 *
275
 * @return      Zero if the call doesn't match any connection.
275
 * @return      Zero if the call doesn't match any connection.
276
 *          One if the call was passed to the respective connection
276
 *          One if the call was passed to the respective connection
277
 *          fibril.
277
 *          fibril.
278
 */
278
 */
279
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)
280
{
280
{
281
    connection_t *conn;
281
    connection_t *conn;
282
    msg_t *msg;
282
    msg_t *msg;
283
    link_t *hlp;
283
    link_t *hlp;
284
    unsigned long key;
284
    unsigned long key;
285
 
285
 
286
    futex_down(&async_futex);
286
    futex_down(&async_futex);
287
 
287
 
288
    key = call->in_phone_hash;
288
    key = call->in_phone_hash;
289
    hlp = hash_table_find(&conn_hash_table, &key);
289
    hlp = hash_table_find(&conn_hash_table, &key);
290
    if (!hlp) {
290
    if (!hlp) {
291
        futex_up(&async_futex);
291
        futex_up(&async_futex);
292
        return 0;
292
        return 0;
293
    }
293
    }
294
    conn = hash_table_get_instance(hlp, connection_t, link);
294
    conn = hash_table_get_instance(hlp, connection_t, link);
295
 
295
 
296
    msg = malloc(sizeof(*msg));
296
    msg = malloc(sizeof(*msg));
297
    msg->callid = callid;
297
    msg->callid = callid;
298
    msg->call = *call;
298
    msg->call = *call;
299
    list_append(&msg->link, &conn->msg_queue);
299
    list_append(&msg->link, &conn->msg_queue);
300
 
300
 
301
    if (IPC_GET_METHOD(*call) == IPC_M_PHONE_HUNGUP)
301
    if (IPC_GET_METHOD(*call) == IPC_M_PHONE_HUNGUP)
302
        conn->close_callid = callid;
302
        conn->close_callid = callid;
303
   
303
   
304
    /* If the connection fibril is waiting for an event, activate it */
304
    /* If the connection fibril is waiting for an event, activate it */
305
    if (!conn->wdata.active) {
305
    if (!conn->wdata.active) {
306
        /* If in timeout list, remove it */
306
        /* If in timeout list, remove it */
307
        if (conn->wdata.inlist) {
307
        if (conn->wdata.inlist) {
308
            conn->wdata.inlist = 0;
308
            conn->wdata.inlist = 0;
309
            list_remove(&conn->wdata.link);
309
            list_remove(&conn->wdata.link);
310
        }
310
        }
311
        conn->wdata.active = 1;
311
        conn->wdata.active = 1;
312
        fibril_add_ready(conn->wdata.fid);
312
        fibril_add_ready(conn->wdata.fid);
313
    }
313
    }
314
 
314
 
315
    futex_up(&async_futex);
315
    futex_up(&async_futex);
316
 
316
 
317
    return 1;
317
    return 1;
318
}
318
}
319
 
319
 
320
/** Return new incoming message for the current (fibril-local) connection.
320
/** Return new incoming message for the current (fibril-local) connection.
321
 *
321
 *
322
 * @param call      Storage where the incoming call data will be stored.
322
 * @param call      Storage where the incoming call data will be stored.
323
 * @param usecs     Timeout in microseconds. Zero denotes no timeout.
323
 * @param usecs     Timeout in microseconds. Zero denotes no timeout.
324
 *
324
 *
325
 * @return      If no timeout was specified, then a hash of the
325
 * @return      If no timeout was specified, then a hash of the
326
 *          incoming call is returned. If a timeout is specified,
326
 *          incoming call is returned. If a timeout is specified,
327
 *          then a hash of the incoming call is returned unless
327
 *          then a hash of the incoming call is returned unless
328
 *          the timeout expires prior to receiving a message. In
328
 *          the timeout expires prior to receiving a message. In
329
 *          that case zero is returned.
329
 *          that case zero is returned.
330
 */
330
 */
331
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)
332
{
332
{
333
    msg_t *msg;
333
    msg_t *msg;
334
    ipc_callid_t callid;
334
    ipc_callid_t callid;
335
    connection_t *conn;
335
    connection_t *conn;
336
   
336
   
337
    assert(FIBRIL_connection);
337
    assert(FIBRIL_connection);
338
    /* GCC 4.1.0 coughs on FIBRIL_connection-> dereference,
338
    /* GCC 4.1.0 coughs on FIBRIL_connection-> dereference,
339
     * 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.
340
     *           I would never expect to find so many errors in
340
     *           I would never expect to find so many errors in
341
     *           a compiler *($&$(*&$
341
     *           a compiler *($&$(*&$
342
     */
342
     */
343
    conn = FIBRIL_connection;
343
    conn = FIBRIL_connection;
344
 
344
 
345
    futex_down(&async_futex);
345
    futex_down(&async_futex);
346
 
346
 
347
    if (usecs) {
347
    if (usecs) {
348
        gettimeofday(&conn->wdata.expires, NULL);
348
        gettimeofday(&conn->wdata.expires, NULL);
349
        tv_add(&conn->wdata.expires, usecs);
349
        tv_add(&conn->wdata.expires, usecs);
350
    } else {
350
    } else {
351
        conn->wdata.inlist = 0;
351
        conn->wdata.inlist = 0;
352
    }
352
    }
353
    /* If nothing in queue, wait until something arrives */
353
    /* If nothing in queue, wait until something arrives */
354
    while (list_empty(&conn->msg_queue)) {
354
    while (list_empty(&conn->msg_queue)) {
355
        if (usecs)
355
        if (usecs)
356
            insert_timeout(&conn->wdata);
356
            insert_timeout(&conn->wdata);
357
 
357
 
358
        conn->wdata.active = 0;
358
        conn->wdata.active = 0;
-
 
359
        /*
-
 
360
         * Note: the current fibril will be rescheduled either due to a
-
 
361
         * timeout or due to an arriving message destined to it. In the
-
 
362
         * former case, handle_expired_timeouts() and, in the latter
-
 
363
         * case, route_call() will perform the wakeup.
-
 
364
         */
359
        fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
365
        fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
360
        /*
366
        /*
361
         * Futex is up after getting back from async_manager get it
367
         * Futex is up after getting back from async_manager get it
362
         * again.
368
         * again.
363
        */
369
         */
364
        futex_down(&async_futex);
370
        futex_down(&async_futex);
365
        if (usecs && conn->wdata.timedout &&
371
        if (usecs && conn->wdata.timedout &&
366
            list_empty(&conn->msg_queue)) {
372
            list_empty(&conn->msg_queue)) {
367
            /* If we timed out -> exit */
373
            /* If we timed out -> exit */
368
            futex_up(&async_futex);
374
            futex_up(&async_futex);
369
            return 0;
375
            return 0;
370
        }
376
        }
371
    }
377
    }
372
   
378
   
373
    msg = list_get_instance(conn->msg_queue.next, msg_t, link);
379
    msg = list_get_instance(conn->msg_queue.next, msg_t, link);
374
    list_remove(&msg->link);
380
    list_remove(&msg->link);
375
    callid = msg->callid;
381
    callid = msg->callid;
376
    *call = msg->call;
382
    *call = msg->call;
377
    free(msg);
383
    free(msg);
378
   
384
   
379
    futex_up(&async_futex);
385
    futex_up(&async_futex);
380
    return callid;
386
    return callid;
381
}
387
}
382
 
388
 
383
/** Default fibril function that gets called to handle new connection.
389
/** Default fibril function that gets called to handle new connection.
384
 *
390
 *
385
 * This function is defined as a weak symbol - to be redefined in user code.
391
 * This function is defined as a weak symbol - to be redefined in user code.
386
 *
392
 *
387
 * @param callid    Hash of the incoming call.
393
 * @param callid    Hash of the incoming call.
388
 * @param call      Data of the incoming call.
394
 * @param call      Data of the incoming call.
389
 */
395
 */
390
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)
396
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)
391
{
397
{
392
    ipc_answer_fast(callid, ENOENT, 0, 0);
398
    ipc_answer_fast(callid, ENOENT, 0, 0);
393
}
399
}
394
 
400
 
395
/** Default fibril function that gets called to handle interrupt notifications.
401
/** Default fibril function that gets called to handle interrupt notifications.
396
 *
402
 *
397
 * @param callid    Hash of the incoming call.
403
 * @param callid    Hash of the incoming call.
398
 * @param call      Data of the incoming call.
404
 * @param call      Data of the incoming call.
399
 */
405
 */
400
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
406
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
401
{
407
{
402
}
408
}
403
 
409
 
404
/** Wrapper for client connection fibril.
410
/** Wrapper for client connection fibril.
405
 *
411
 *
406
 * When a new connection arrives, a fibril with this implementing function is
412
 * When a new connection arrives, a fibril with this implementing function is
407
 * created. It calls client_connection() and does the final cleanup.
413
 * created. It calls client_connection() and does the final cleanup.
408
 *
414
 *
409
 * @param arg       Connection structure pointer.
415
 * @param arg       Connection structure pointer.
410
 *
416
 *
411
 * @return      Always zero.
417
 * @return      Always zero.
412
 */
418
 */
413
static int connection_fibril(void  *arg)
419
static int connection_fibril(void  *arg)
414
{
420
{
415
    unsigned long key;
421
    unsigned long key;
416
    msg_t *msg;
422
    msg_t *msg;
417
    int close_answered = 0;
423
    int close_answered = 0;
418
 
424
 
419
    /* Setup fibril-local connection pointer */
425
    /* Setup fibril-local connection pointer */
420
    FIBRIL_connection = (connection_t *) arg;
426
    FIBRIL_connection = (connection_t *) arg;
421
    FIBRIL_connection->cfibril(FIBRIL_connection->callid,
427
    FIBRIL_connection->cfibril(FIBRIL_connection->callid,
422
        &FIBRIL_connection->call);
428
        &FIBRIL_connection->call);
423
   
429
   
424
    /* Remove myself from the connection hash table */
430
    /* Remove myself from the connection hash table */
425
    futex_down(&async_futex);
431
    futex_down(&async_futex);
426
    key = FIBRIL_connection->in_phone_hash;
432
    key = FIBRIL_connection->in_phone_hash;
427
    hash_table_remove(&conn_hash_table, &key, 1);
433
    hash_table_remove(&conn_hash_table, &key, 1);
428
    futex_up(&async_futex);
434
    futex_up(&async_futex);
429
   
435
   
430
    /* Answer all remaining messages with EHANGUP */
436
    /* Answer all remaining messages with EHANGUP */
431
    while (!list_empty(&FIBRIL_connection->msg_queue)) {
437
    while (!list_empty(&FIBRIL_connection->msg_queue)) {
432
        msg = list_get_instance(FIBRIL_connection->msg_queue.next,
438
        msg = list_get_instance(FIBRIL_connection->msg_queue.next,
433
            msg_t, link);
439
            msg_t, link);
434
        list_remove(&msg->link);
440
        list_remove(&msg->link);
435
        if (msg->callid == FIBRIL_connection->close_callid)
441
        if (msg->callid == FIBRIL_connection->close_callid)
436
            close_answered = 1;
442
            close_answered = 1;
437
        ipc_answer_fast(msg->callid, EHANGUP, 0, 0);
443
        ipc_answer_fast(msg->callid, EHANGUP, 0, 0);
438
        free(msg);
444
        free(msg);
439
    }
445
    }
440
    if (FIBRIL_connection->close_callid)
446
    if (FIBRIL_connection->close_callid)
441
        ipc_answer_fast(FIBRIL_connection->close_callid, 0, 0, 0);
447
        ipc_answer_fast(FIBRIL_connection->close_callid, 0, 0, 0);
442
   
448
   
443
    return 0;
449
    return 0;
444
}
450
}
445
 
451
 
446
/** Create a new fibril for a new connection.
452
/** Create a new fibril for a new connection.
447
 *
453
 *
448
 * Creates new fibril for connection, fills in connection structures and inserts
454
 * Creates new fibril for connection, fills in connection structures and inserts
449
 * it into the hash table, so that later we can easily do routing of messages to
455
 * it into the hash table, so that later we can easily do routing of messages to
450
 * particular fibrils.
456
 * particular fibrils.
451
 *
457
 *
452
 * @param in_phone_hash Identification of the incoming connection.
458
 * @param in_phone_hash Identification of the incoming connection.
453
 * @param callid    Hash of the opening IPC_M_CONNECT_ME_TO call.
459
 * @param callid    Hash of the opening IPC_M_CONNECT_ME_TO call.
454
 * @param call      Call data of the opening call.
460
 * @param call      Call data of the opening call.
455
 * @param cfibril   Fibril function that should be called upon opening the
461
 * @param cfibril   Fibril function that should be called upon opening the
456
 *          connection.
462
 *          connection.
457
 *
463
 *
458
 * @return      New fibril id or NULL on failure.
464
 * @return      New fibril id or NULL on failure.
459
 */
465
 */
460
fid_t async_new_connection(ipcarg_t in_phone_hash, ipc_callid_t callid,
466
fid_t async_new_connection(ipcarg_t in_phone_hash, ipc_callid_t callid,
461
    ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *))
467
    ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *))
462
{
468
{
463
    connection_t *conn;
469
    connection_t *conn;
464
    unsigned long key;
470
    unsigned long key;
465
 
471
 
466
    conn = malloc(sizeof(*conn));
472
    conn = malloc(sizeof(*conn));
467
    if (!conn) {
473
    if (!conn) {
468
        ipc_answer_fast(callid, ENOMEM, 0, 0);
474
        ipc_answer_fast(callid, ENOMEM, 0, 0);
469
        return NULL;
475
        return NULL;
470
    }
476
    }
471
    conn->in_phone_hash = in_phone_hash;
477
    conn->in_phone_hash = in_phone_hash;
472
    list_initialize(&conn->msg_queue);
478
    list_initialize(&conn->msg_queue);
473
    conn->callid = callid;
479
    conn->callid = callid;
474
    conn->close_callid = 0;
480
    conn->close_callid = 0;
475
    if (call)
481
    if (call)
476
        conn->call = *call;
482
        conn->call = *call;
477
    conn->wdata.active = 1; /* We will activate the fibril ASAP */
483
    conn->wdata.active = 1; /* We will activate the fibril ASAP */
478
    conn->cfibril = cfibril;
484
    conn->cfibril = cfibril;
479
 
485
 
480
    conn->wdata.fid = fibril_create(connection_fibril, conn);
486
    conn->wdata.fid = fibril_create(connection_fibril, conn);
481
    if (!conn->wdata.fid) {
487
    if (!conn->wdata.fid) {
482
        free(conn);
488
        free(conn);
483
        ipc_answer_fast(callid, ENOMEM, 0, 0);
489
        ipc_answer_fast(callid, ENOMEM, 0, 0);
484
        return NULL;
490
        return NULL;
485
    }
491
    }
486
    /* Add connection to the connection hash table */
492
    /* Add connection to the connection hash table */
487
    key = conn->in_phone_hash;
493
    key = conn->in_phone_hash;
488
    futex_down(&async_futex);
494
    futex_down(&async_futex);
489
    hash_table_insert(&conn_hash_table, &key, &conn->link);
495
    hash_table_insert(&conn_hash_table, &key, &conn->link);
490
    futex_up(&async_futex);
496
    futex_up(&async_futex);
491
 
497
 
492
    fibril_add_ready(conn->wdata.fid);
498
    fibril_add_ready(conn->wdata.fid);
493
 
499
 
494
    return conn->wdata.fid;
500
    return conn->wdata.fid;
495
}
501
}
496
 
502
 
497
/** Handle a call that was received.
503
/** Handle a call that was received.
498
 *
504
 *
499
 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
505
 * 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.
506
 * Otherwise the call is routed to its connection fibril.
501
 *
507
 *
502
 * @param callid    Hash of the incoming call.
508
 * @param callid    Hash of the incoming call.
503
 * @param call      Data of the incoming call.
509
 * @param call      Data of the incoming call.
504
 */
510
 */
505
static void handle_call(ipc_callid_t callid, ipc_call_t *call)
511
static void handle_call(ipc_callid_t callid, ipc_call_t *call)
506
{
512
{
507
    /* Unrouted call - do some default behaviour */
513
    /* Unrouted call - do some default behaviour */
508
    if ((callid & IPC_CALLID_NOTIFICATION)) {
514
    if ((callid & IPC_CALLID_NOTIFICATION)) {
509
        in_interrupt_handler = 1;
515
        in_interrupt_handler = 1;
510
        (*interrupt_received)(callid,call);
516
        (*interrupt_received)(callid,call);
511
        in_interrupt_handler = 0;
517
        in_interrupt_handler = 0;
512
        return;
518
        return;
513
    }      
519
    }      
514
 
520
 
515
    switch (IPC_GET_METHOD(*call)) {
521
    switch (IPC_GET_METHOD(*call)) {
516
    case IPC_M_CONNECT_ME_TO:
522
    case IPC_M_CONNECT_ME_TO:
517
        /* Open new connection with fibril etc. */
523
        /* Open new connection with fibril etc. */
518
        async_new_connection(IPC_GET_ARG3(*call), callid, call,
524
        async_new_connection(IPC_GET_ARG3(*call), callid, call,
519
            client_connection);
525
            client_connection);
520
        return;
526
        return;
521
    }
527
    }
522
 
528
 
523
    /* Try to route the call through the connection hash table */
529
    /* Try to route the call through the connection hash table */
524
    if (route_call(callid, call))
530
    if (route_call(callid, call))
525
        return;
531
        return;
526
 
532
 
527
    /* Unknown call from unknown phone - hang it up */
533
    /* Unknown call from unknown phone - hang it up */
528
    ipc_answer_fast(callid, EHANGUP, 0, 0);
534
    ipc_answer_fast(callid, EHANGUP, 0, 0);
529
}
535
}
530
 
536
 
531
/** Fire all timeouts that expired. */
537
/** Fire all timeouts that expired. */
532
static void handle_expired_timeouts(void)
538
static void handle_expired_timeouts(void)
533
{
539
{
534
    struct timeval tv;
540
    struct timeval tv;
535
    awaiter_t *waiter;
541
    awaiter_t *waiter;
536
    link_t *cur;
542
    link_t *cur;
537
 
543
 
538
    gettimeofday(&tv, NULL);
544
    gettimeofday(&tv, NULL);
539
    futex_down(&async_futex);
545
    futex_down(&async_futex);
540
 
546
 
541
    cur = timeout_list.next;
547
    cur = timeout_list.next;
542
    while (cur != &timeout_list) {
548
    while (cur != &timeout_list) {
543
        waiter = list_get_instance(cur, awaiter_t, link);
549
        waiter = list_get_instance(cur, awaiter_t, link);
544
        if (tv_gt(&waiter->expires, &tv))
550
        if (tv_gt(&waiter->expires, &tv))
545
            break;
551
            break;
546
        cur = cur->next;
552
        cur = cur->next;
547
        list_remove(&waiter->link);
553
        list_remove(&waiter->link);
548
        waiter->inlist = 0;
554
        waiter->inlist = 0;
549
        waiter->timedout = 1;
555
        waiter->timedout = 1;
550
        /*
556
        /*
551
         * Redundant condition?
557
         * Redundant condition?
552
         * The fibril should not be active when it gets here.
558
         * The fibril should not be active when it gets here.
553
         */
559
         */
554
        if (!waiter->active) {
560
        if (!waiter->active) {
555
            waiter->active = 1;
561
            waiter->active = 1;
556
            fibril_add_ready(waiter->fid);
562
            fibril_add_ready(waiter->fid);
557
        }
563
        }
558
    }
564
    }
559
 
565
 
560
    futex_up(&async_futex);
566
    futex_up(&async_futex);
561
}
567
}
562
 
568
 
563
/** Endless loop dispatching incoming calls and answers.
569
/** Endless loop dispatching incoming calls and answers.
564
 *
570
 *
565
 * @return      Never returns.
571
 * @return      Never returns.
566
 */
572
 */
567
static int async_manager_worker(void)
573
static int async_manager_worker(void)
568
{
574
{
569
    ipc_call_t call;
575
    ipc_call_t call;
570
    ipc_callid_t callid;
576
    ipc_callid_t callid;
571
    int timeout;
577
    int timeout;
572
    awaiter_t *waiter;
578
    awaiter_t *waiter;
573
    struct timeval tv;
579
    struct timeval tv;
574
 
580
 
575
    while (1) {
581
    while (1) {
576
        if (fibril_schedule_next_adv(FIBRIL_FROM_MANAGER)) {
582
        if (fibril_schedule_next_adv(FIBRIL_FROM_MANAGER)) {
577
            futex_up(&async_futex);
583
            futex_up(&async_futex);
578
            /*
584
            /*
579
             * async_futex is always held when entering a manager
585
             * async_futex is always held when entering a manager
580
             * fibril.
586
             * fibril.
581
             */
587
             */
582
            continue;
588
            continue;
583
        }
589
        }
584
        futex_down(&async_futex);
590
        futex_down(&async_futex);
585
        if (!list_empty(&timeout_list)) {
591
        if (!list_empty(&timeout_list)) {
586
            waiter = list_get_instance(timeout_list.next, awaiter_t,
592
            waiter = list_get_instance(timeout_list.next, awaiter_t,
587
                link);
593
                link);
588
            gettimeofday(&tv, NULL);
594
            gettimeofday(&tv, NULL);
589
            if (tv_gteq(&tv, &waiter->expires)) {
595
            if (tv_gteq(&tv, &waiter->expires)) {
590
                futex_up(&async_futex);
596
                futex_up(&async_futex);
591
                handle_expired_timeouts();
597
                handle_expired_timeouts();
592
                continue;
598
                continue;
593
            } else
599
            } else
594
                timeout = tv_sub(&waiter->expires, &tv);
600
                timeout = tv_sub(&waiter->expires, &tv);
595
        } else
601
        } else
596
            timeout = SYNCH_NO_TIMEOUT;
602
            timeout = SYNCH_NO_TIMEOUT;
597
        futex_up(&async_futex);
603
        futex_up(&async_futex);
598
 
604
 
599
        callid = ipc_wait_cycle(&call, timeout, SYNCH_FLAGS_NONE);
605
        callid = ipc_wait_cycle(&call, timeout, SYNCH_FLAGS_NONE);
600
 
606
 
601
        if (!callid) {
607
        if (!callid) {
602
            handle_expired_timeouts();
608
            handle_expired_timeouts();
603
            continue;
609
            continue;
604
        }
610
        }
605
 
611
 
606
        if (callid & IPC_CALLID_ANSWERED) {
612
        if (callid & IPC_CALLID_ANSWERED) {
607
            continue;
613
            continue;
608
        }
614
        }
609
 
615
 
610
        handle_call(callid, &call);
616
        handle_call(callid, &call);
611
    }
617
    }
612
   
618
   
613
    return 0;
619
    return 0;
614
}
620
}
615
 
621
 
616
/** Function to start async_manager as a standalone fibril.
622
/** Function to start async_manager as a standalone fibril.
617
 *
623
 *
618
 * When more kernel threads are used, one async manager should exist per thread.
624
 * When more kernel threads are used, one async manager should exist per thread.
619
 *
625
 *
620
 * @param arg       Unused.
626
 * @param arg       Unused.
621
 *
627
 *
622
 * @return      Never returns.
628
 * @return      Never returns.
623
 */
629
 */
624
static int async_manager_fibril(void *arg)
630
static int async_manager_fibril(void *arg)
625
{
631
{
626
    futex_up(&async_futex);
632
    futex_up(&async_futex);
627
    /*
633
    /*
628
     * async_futex is always locked when entering manager
634
     * async_futex is always locked when entering manager
629
     */
635
     */
630
    async_manager_worker();
636
    async_manager_worker();
631
   
637
   
632
    return 0;
638
    return 0;
633
}
639
}
634
 
640
 
635
/** Add one manager to manager list. */
641
/** Add one manager to manager list. */
636
void async_create_manager(void)
642
void async_create_manager(void)
637
{
643
{
638
    fid_t fid;
644
    fid_t fid;
639
 
645
 
640
    fid = fibril_create(async_manager_fibril, NULL);
646
    fid = fibril_create(async_manager_fibril, NULL);
641
    fibril_add_manager(fid);
647
    fibril_add_manager(fid);
642
}
648
}
643
 
649
 
644
/** Remove one manager from manager list */
650
/** Remove one manager from manager list */
645
void async_destroy_manager(void)
651
void async_destroy_manager(void)
646
{
652
{
647
    fibril_remove_manager();
653
    fibril_remove_manager();
648
}
654
}
649
 
655
 
650
/** Initialize the async framework.
656
/** Initialize the async framework.
651
 *
657
 *
652
 * @return      Zero on success or an error code.
658
 * @return      Zero on success or an error code.
653
 */
659
 */
654
int _async_init(void)
660
int _async_init(void)
655
{
661
{
656
    if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
662
    if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
657
        &conn_hash_table_ops)) {
663
        &conn_hash_table_ops)) {
658
        printf("%s: cannot create hash table\n", "async");
664
        printf("%s: cannot create hash table\n", "async");
659
        return ENOMEM;
665
        return ENOMEM;
660
    }
666
    }
661
   
667
   
662
    return 0;
668
    return 0;
663
}
669
}
664
 
670
 
665
/** Reply received callback.
671
/** Reply received callback.
666
 *
672
 *
667
 * This function is called whenever a reply for an asynchronous message sent out
673
 * This function is called whenever a reply for an asynchronous message sent out
668
 * by the asynchronous framework is received.
674
 * by the asynchronous framework is received.
669
 *
675
 *
670
 * Notify the fibril which is waiting for this message that it has arrived.
676
 * Notify the fibril which is waiting for this message that it has arrived.
671
 *
677
 *
672
 * @param private   Pointer to the asynchronous message record.
678
 * @param private   Pointer to the asynchronous message record.
673
 * @param retval    Value returned in the answer.
679
 * @param retval    Value returned in the answer.
674
 * @param data      Call data of the answer.
680
 * @param data      Call data of the answer.
675
 */
681
 */
676
static void reply_received(void *private, int retval, ipc_call_t *data)
682
static void reply_received(void *private, int retval, ipc_call_t *data)
677
{
683
{
678
    amsg_t *msg = (amsg_t *) private;
684
    amsg_t *msg = (amsg_t *) private;
679
 
685
 
680
    msg->retval = retval;
686
    msg->retval = retval;
681
 
687
 
682
    futex_down(&async_futex);
688
    futex_down(&async_futex);
683
    /* Copy data after futex_down, just in case the call was detached */
689
    /* Copy data after futex_down, just in case the call was detached */
684
    if (msg->dataptr)
690
    if (msg->dataptr)
685
        *msg->dataptr = *data;
691
        *msg->dataptr = *data;
686
 
692
 
687
    write_barrier();
693
    write_barrier();
688
    /* Remove message from timeout list */
694
    /* Remove message from timeout list */
689
    if (msg->wdata.inlist)
695
    if (msg->wdata.inlist)
690
        list_remove(&msg->wdata.link);
696
        list_remove(&msg->wdata.link);
691
    msg->done = 1;
697
    msg->done = 1;
692
    if (!msg->wdata.active) {
698
    if (!msg->wdata.active) {
693
        msg->wdata.active = 1;
699
        msg->wdata.active = 1;
694
        fibril_add_ready(msg->wdata.fid);
700
        fibril_add_ready(msg->wdata.fid);
695
    }
701
    }
696
    futex_up(&async_futex);
702
    futex_up(&async_futex);
697
}
703
}
698
 
704
 
699
/** Send message and return id of the sent message.
705
/** Send message and return id of the sent message.
700
 *
706
 *
701
 * The return value can be used as input for async_wait() to wait for
707
 * The return value can be used as input for async_wait() to wait for
702
 * completion.
708
 * completion.
703
 *
709
 *
704
 * @param phoneid   Handle of the phone that will be used for the send.
710
 * @param phoneid   Handle of the phone that will be used for the send.
705
 * @param method    Service-defined method.
711
 * @param method    Service-defined method.
706
 * @param arg1      Service-defined payload argument.
712
 * @param arg1      Service-defined payload argument.
707
 * @param arg2      Service-defined payload argument.
713
 * @param arg2      Service-defined payload argument.
708
 * @param dataptr   If non-NULL, storage where the reply data will be
714
 * @param dataptr   If non-NULL, storage where the reply data will be
709
 *          stored.
715
 *          stored.
710
 *
716
 *
711
 * @return      Hash of the sent message.
717
 * @return      Hash of the sent message.
712
 */
718
 */
713
aid_t async_send_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
719
aid_t async_send_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
714
    ipc_call_t *dataptr)
720
    ipc_call_t *dataptr)
715
{
721
{
716
    amsg_t *msg;
722
    amsg_t *msg;
717
 
723
 
718
    if (in_interrupt_handler) {
724
    if (in_interrupt_handler) {
719
        printf("Cannot send asynchronous request in interrupt "
725
        printf("Cannot send asynchronous request in interrupt "
720
            "handler.\n");
726
            "handler.\n");
721
        _exit(1);
727
        _exit(1);
722
    }
728
    }
723
 
729
 
724
    msg = malloc(sizeof(*msg));
730
    msg = malloc(sizeof(*msg));
725
    msg->done = 0;
731
    msg->done = 0;
726
    msg->dataptr = dataptr;
732
    msg->dataptr = dataptr;
727
 
733
 
728
    /* We may sleep in the next method, but it will use its own mechanism */
734
    /* We may sleep in the next method, but it will use its own mechanism */
729
    msg->wdata.active = 1;
735
    msg->wdata.active = 1;
730
               
736
               
731
    ipc_call_async_2(phoneid, method, arg1, arg2, msg, reply_received, 1);
737
    ipc_call_async_2(phoneid, method, arg1, arg2, msg, reply_received, 1);
732
 
738
 
733
    return (aid_t) msg;
739
    return (aid_t) msg;
734
}
740
}
735
 
741
 
736
/** Send message and return id of the sent message
742
/** Send message and return id of the sent message
737
 *
743
 *
738
 * The return value can be used as input for async_wait() to wait for
744
 * The return value can be used as input for async_wait() to wait for
739
 * completion.
745
 * completion.
740
 *
746
 *
741
 * @param phoneid   Handle of the phone that will be used for the send.
747
 * @param phoneid   Handle of the phone that will be used for the send.
742
 * @param method    Service-defined method.
748
 * @param method    Service-defined method.
743
 * @param arg1      Service-defined payload argument.
749
 * @param arg1      Service-defined payload argument.
744
 * @param arg2      Service-defined payload argument.
750
 * @param arg2      Service-defined payload argument.
745
 * @param arg3      Service-defined payload argument.
751
 * @param arg3      Service-defined payload argument.
746
 * @param dataptr   If non-NULL, storage where the reply data will be
752
 * @param dataptr   If non-NULL, storage where the reply data will be
747
 *          stored.
753
 *          stored.
748
 *
754
 *
749
 * @return      Hash of the sent message.
755
 * @return      Hash of the sent message.
750
 */
756
 */
751
aid_t async_send_3(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
757
aid_t async_send_3(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
752
    ipcarg_t arg3, ipc_call_t *dataptr)
758
    ipcarg_t arg3, ipc_call_t *dataptr)
753
{
759
{
754
    amsg_t *msg;
760
    amsg_t *msg;
755
 
761
 
756
    if (in_interrupt_handler) {
762
    if (in_interrupt_handler) {
757
        printf("Cannot send asynchronous request in interrupt "
763
        printf("Cannot send asynchronous request in interrupt "
758
            "handler.\n");
764
            "handler.\n");
759
        _exit(1);
765
        _exit(1);
760
    }
766
    }
761
 
767
 
762
    msg = malloc(sizeof(*msg));
768
    msg = malloc(sizeof(*msg));
763
    msg->done = 0;
769
    msg->done = 0;
764
    msg->dataptr = dataptr;
770
    msg->dataptr = dataptr;
765
 
771
 
766
    /* We may sleep in next method, but it will use its own mechanism */
772
    /* We may sleep in next method, but it will use its own mechanism */
767
    msg->wdata.active = 1;
773
    msg->wdata.active = 1;
768
               
774
               
769
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, msg, reply_received,
775
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, msg, reply_received,
770
        1);
776
        1);
771
 
777
 
772
    return (aid_t) msg;
778
    return (aid_t) msg;
773
}
779
}
774
 
780
 
775
/** Wait for a message sent by the async framework.
781
/** Wait for a message sent by the async framework.
776
 *
782
 *
777
 * @param amsgid    Hash of the message to wait for.
783
 * @param amsgid    Hash of the message to wait for.
778
 * @param retval    Pointer to storage where the retval of the answer will
784
 * @param retval    Pointer to storage where the retval of the answer will
779
 *          be stored.
785
 *          be stored.
780
 */
786
 */
781
void async_wait_for(aid_t amsgid, ipcarg_t *retval)
787
void async_wait_for(aid_t amsgid, ipcarg_t *retval)
782
{
788
{
783
    amsg_t *msg = (amsg_t *) amsgid;
789
    amsg_t *msg = (amsg_t *) amsgid;
784
 
790
 
785
    futex_down(&async_futex);
791
    futex_down(&async_futex);
786
    if (msg->done) {
792
    if (msg->done) {
787
        futex_up(&async_futex);
793
        futex_up(&async_futex);
788
        goto done;
794
        goto done;
789
    }
795
    }
790
 
796
 
791
    msg->wdata.fid = fibril_get_id();
797
    msg->wdata.fid = fibril_get_id();
792
    msg->wdata.active = 0;
798
    msg->wdata.active = 0;
793
    msg->wdata.inlist = 0;
799
    msg->wdata.inlist = 0;
794
    /* Leave the async_futex locked when entering this function */
800
    /* Leave the async_futex locked when entering this function */
795
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
801
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
796
    /* futex is up automatically after fibril_schedule_next...*/
802
    /* futex is up automatically after fibril_schedule_next...*/
797
done:
803
done:
798
    if (retval)
804
    if (retval)
799
        *retval = msg->retval;
805
        *retval = msg->retval;
800
    free(msg);
806
    free(msg);
801
}
807
}
802
 
808
 
803
/** Wait for a message sent by the async framework, timeout variant.
809
/** Wait for a message sent by the async framework, timeout variant.
804
 *
810
 *
805
 * @param amsgid    Hash of the message to wait for.
811
 * @param amsgid    Hash of the message to wait for.
806
 * @param retval    Pointer to storage where the retval of the answer will
812
 * @param retval    Pointer to storage where the retval of the answer will
807
 *          be stored.
813
 *          be stored.
808
 * @param timeout   Timeout in microseconds.
814
 * @param timeout   Timeout in microseconds.
809
 *
815
 *
810
 * @return      Zero on success, ETIMEOUT if the timeout has expired.
816
 * @return      Zero on success, ETIMEOUT if the timeout has expired.
811
 */
817
 */
812
int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout)
818
int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout)
813
{
819
{
814
    amsg_t *msg = (amsg_t *) amsgid;
820
    amsg_t *msg = (amsg_t *) amsgid;
815
 
821
 
816
    /* TODO: Let it go through the event read at least once */
822
    /* TODO: Let it go through the event read at least once */
817
    if (timeout < 0)
823
    if (timeout < 0)
818
        return ETIMEOUT;
824
        return ETIMEOUT;
819
 
825
 
820
    futex_down(&async_futex);
826
    futex_down(&async_futex);
821
    if (msg->done) {
827
    if (msg->done) {
822
        futex_up(&async_futex);
828
        futex_up(&async_futex);
823
        goto done;
829
        goto done;
824
    }
830
    }
825
 
831
 
826
    gettimeofday(&msg->wdata.expires, NULL);
832
    gettimeofday(&msg->wdata.expires, NULL);
827
    tv_add(&msg->wdata.expires, timeout);
833
    tv_add(&msg->wdata.expires, timeout);
828
 
834
 
829
    msg->wdata.fid = fibril_get_id();
835
    msg->wdata.fid = fibril_get_id();
830
    msg->wdata.active = 0;
836
    msg->wdata.active = 0;
831
    insert_timeout(&msg->wdata);
837
    insert_timeout(&msg->wdata);
832
 
838
 
833
    /* Leave the async_futex locked when entering this function */
839
    /* Leave the async_futex locked when entering this function */
834
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
840
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
835
    /* futex is up automatically after fibril_schedule_next...*/
841
    /* futex is up automatically after fibril_schedule_next...*/
836
 
842
 
837
    if (!msg->done)
843
    if (!msg->done)
838
        return ETIMEOUT;
844
        return ETIMEOUT;
839
 
845
 
840
done:
846
done:
841
    if (retval)
847
    if (retval)
842
        *retval = msg->retval;
848
        *retval = msg->retval;
843
    free(msg);
849
    free(msg);
844
 
850
 
845
    return 0;
851
    return 0;
846
}
852
}
847
 
853
 
848
/** Wait for specified time.
854
/** Wait for specified time.
849
 *
855
 *
850
 * The current fibril is suspended but the thread continues to execute.
856
 * The current fibril is suspended but the thread continues to execute.
851
 *
857
 *
852
 * @param timeout   Duration of the wait in microseconds.
858
 * @param timeout   Duration of the wait in microseconds.
853
 */
859
 */
854
void async_usleep(suseconds_t timeout)
860
void async_usleep(suseconds_t timeout)
855
{
861
{
856
    amsg_t *msg;
862
    amsg_t *msg;
857
   
863
   
858
    if (in_interrupt_handler) {
864
    if (in_interrupt_handler) {
859
        printf("Cannot call async_usleep in interrupt handler.\n");
865
        printf("Cannot call async_usleep in interrupt handler.\n");
860
        _exit(1);
866
        _exit(1);
861
    }
867
    }
862
 
868
 
863
    msg = malloc(sizeof(*msg));
869
    msg = malloc(sizeof(*msg));
864
    if (!msg)
870
    if (!msg)
865
        return;
871
        return;
866
 
872
 
867
    msg->wdata.fid = fibril_get_id();
873
    msg->wdata.fid = fibril_get_id();
868
    msg->wdata.active = 0;
874
    msg->wdata.active = 0;
869
 
875
 
870
    gettimeofday(&msg->wdata.expires, NULL);
876
    gettimeofday(&msg->wdata.expires, NULL);
871
    tv_add(&msg->wdata.expires, timeout);
877
    tv_add(&msg->wdata.expires, timeout);
872
 
878
 
873
    futex_down(&async_futex);
879
    futex_down(&async_futex);
874
    insert_timeout(&msg->wdata);
880
    insert_timeout(&msg->wdata);
875
    /* Leave the async_futex locked when entering this function */
881
    /* Leave the async_futex locked when entering this function */
876
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
882
    fibril_schedule_next_adv(FIBRIL_TO_MANAGER);
877
    /* futex is up automatically after fibril_schedule_next_adv()...*/
883
    /* futex is up automatically after fibril_schedule_next_adv()...*/
878
    free(msg);
884
    free(msg);
879
}
885
}
880
 
886
 
881
/** Setter for client_connection function pointer.
887
/** Setter for client_connection function pointer.
882
 *
888
 *
883
 * @param conn      Function that will implement a new connection fibril.
889
 * @param conn      Function that will implement a new connection fibril.
884
 */
890
 */
885
void async_set_client_connection(async_client_conn_t conn)
891
void async_set_client_connection(async_client_conn_t conn)
886
{
892
{
887
    client_connection = conn;
893
    client_connection = conn;
888
}
894
}
889
 
895
 
890
/** Setter for interrupt_received function pointer.
896
/** Setter for interrupt_received function pointer.
891
 *
897
 *
892
 * @param conn      Function that will implement a new interrupt
898
 * @param conn      Function that will implement a new interrupt
893
 *          notification fibril.
899
 *          notification fibril.
894
 */
900
 */
895
void async_set_interrupt_received(async_client_conn_t conn)
901
void async_set_interrupt_received(async_client_conn_t conn)
896
{
902
{
897
    interrupt_received = conn;
903
    interrupt_received = conn;
898
}
904
}
899
 
905
 
900
/* Primitive functions for simple communication */
906
/* Primitive functions for simple communication */
901
void async_msg_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
907
void async_msg_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
902
         ipcarg_t arg2, ipcarg_t arg3)
908
         ipcarg_t arg2, ipcarg_t arg3)
903
{
909
{
904
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, NULL, NULL,
910
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, NULL, NULL,
905
        !in_interrupt_handler);
911
        !in_interrupt_handler);
906
}
912
}
907
 
913
 
908
void async_msg_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2)
914
void async_msg_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2)
909
{
915
{
910
    ipc_call_async_2(phoneid, method, arg1, arg2, NULL, NULL,
916
    ipc_call_async_2(phoneid, method, arg1, arg2, NULL, NULL,
911
        !in_interrupt_handler);
917
        !in_interrupt_handler);
912
}
918
}
913
 
919
 
914
/** @}
920
/** @}
915
 */
921
 */
916
 
922
 
917
 
923