Subversion Repositories HelenOS

Rev

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

Rev 2568 Rev 2569
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
        /*
359
        /*
360
         * Note: the current fibril will be rescheduled either due to a
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
361
         * timeout or due to an arriving message destined to it. In the
362
         * former case, handle_expired_timeouts() and, in the latter
362
         * former case, handle_expired_timeouts() and, in the latter
363
         * case, route_call() will perform the wakeup.
363
         * case, route_call() will perform the wakeup.
364
         */
364
         */
365
        fibril_switch(FIBRIL_TO_MANAGER);
365
        fibril_switch(FIBRIL_TO_MANAGER);
366
        /*
366
        /*
367
         * Futex is up after getting back from async_manager get it
367
         * Futex is up after getting back from async_manager get it
368
         * again.
368
         * again.
369
         */
369
         */
370
        futex_down(&async_futex);
370
        futex_down(&async_futex);
371
        if (usecs && conn->wdata.timedout &&
371
        if (usecs && conn->wdata.timedout &&
372
            list_empty(&conn->msg_queue)) {
372
            list_empty(&conn->msg_queue)) {
373
            /* If we timed out -> exit */
373
            /* If we timed out -> exit */
374
            futex_up(&async_futex);
374
            futex_up(&async_futex);
375
            return 0;
375
            return 0;
376
        }
376
        }
377
    }
377
    }
378
   
378
   
379
    msg = list_get_instance(conn->msg_queue.next, msg_t, link);
379
    msg = list_get_instance(conn->msg_queue.next, msg_t, link);
380
    list_remove(&msg->link);
380
    list_remove(&msg->link);
381
    callid = msg->callid;
381
    callid = msg->callid;
382
    *call = msg->call;
382
    *call = msg->call;
383
    free(msg);
383
    free(msg);
384
   
384
   
385
    futex_up(&async_futex);
385
    futex_up(&async_futex);
386
    return callid;
386
    return callid;
387
}
387
}
388
 
388
 
389
/** Default fibril function that gets called to handle new connection.
389
/** Default fibril function that gets called to handle new connection.
390
 *
390
 *
391
 * 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.
392
 *
392
 *
393
 * @param callid    Hash of the incoming call.
393
 * @param callid    Hash of the incoming call.
394
 * @param call      Data of the incoming call.
394
 * @param call      Data of the incoming call.
395
 */
395
 */
396
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)
397
{
397
{
398
    ipc_answer_fast(callid, ENOENT, 0, 0);
398
    ipc_answer_fast(callid, ENOENT, 0, 0);
399
}
399
}
400
 
400
 
401
/** Default fibril function that gets called to handle interrupt notifications.
401
/** Default fibril function that gets called to handle interrupt notifications.
402
 *
402
 *
403
 * @param callid    Hash of the incoming call.
403
 * @param callid    Hash of the incoming call.
404
 * @param call      Data of the incoming call.
404
 * @param call      Data of the incoming call.
405
 */
405
 */
406
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)
407
{
407
{
408
}
408
}
409
 
409
 
410
/** Wrapper for client connection fibril.
410
/** Wrapper for client connection fibril.
411
 *
411
 *
412
 * 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
413
 * created. It calls client_connection() and does the final cleanup.
413
 * created. It calls client_connection() and does the final cleanup.
414
 *
414
 *
415
 * @param arg       Connection structure pointer.
415
 * @param arg       Connection structure pointer.
416
 *
416
 *
417
 * @return      Always zero.
417
 * @return      Always zero.
418
 */
418
 */
419
static int connection_fibril(void  *arg)
419
static int connection_fibril(void  *arg)
420
{
420
{
421
    unsigned long key;
421
    unsigned long key;
422
    msg_t *msg;
422
    msg_t *msg;
423
    int close_answered = 0;
423
    int close_answered = 0;
424
 
424
 
425
    /* Setup fibril-local connection pointer */
425
    /* Setup fibril-local connection pointer */
426
    FIBRIL_connection = (connection_t *) arg;
426
    FIBRIL_connection = (connection_t *) arg;
427
    FIBRIL_connection->cfibril(FIBRIL_connection->callid,
427
    FIBRIL_connection->cfibril(FIBRIL_connection->callid,
428
        &FIBRIL_connection->call);
428
        &FIBRIL_connection->call);
429
   
429
   
430
    /* Remove myself from the connection hash table */
430
    /* Remove myself from the connection hash table */
431
    futex_down(&async_futex);
431
    futex_down(&async_futex);
432
    key = FIBRIL_connection->in_phone_hash;
432
    key = FIBRIL_connection->in_phone_hash;
433
    hash_table_remove(&conn_hash_table, &key, 1);
433
    hash_table_remove(&conn_hash_table, &key, 1);
434
    futex_up(&async_futex);
434
    futex_up(&async_futex);
435
   
435
   
436
    /* Answer all remaining messages with EHANGUP */
436
    /* Answer all remaining messages with EHANGUP */
437
    while (!list_empty(&FIBRIL_connection->msg_queue)) {
437
    while (!list_empty(&FIBRIL_connection->msg_queue)) {
438
        msg = list_get_instance(FIBRIL_connection->msg_queue.next,
438
        msg = list_get_instance(FIBRIL_connection->msg_queue.next,
439
            msg_t, link);
439
            msg_t, link);
440
        list_remove(&msg->link);
440
        list_remove(&msg->link);
441
        if (msg->callid == FIBRIL_connection->close_callid)
441
        if (msg->callid == FIBRIL_connection->close_callid)
442
            close_answered = 1;
442
            close_answered = 1;
443
        ipc_answer_fast(msg->callid, EHANGUP, 0, 0);
443
        ipc_answer_fast(msg->callid, EHANGUP, 0, 0);
444
        free(msg);
444
        free(msg);
445
    }
445
    }
446
    if (FIBRIL_connection->close_callid)
446
    if (FIBRIL_connection->close_callid)
447
        ipc_answer_fast(FIBRIL_connection->close_callid, 0, 0, 0);
447
        ipc_answer_fast(FIBRIL_connection->close_callid, 0, 0, 0);
448
   
448
   
449
    return 0;
449
    return 0;
450
}
450
}
451
 
451
 
452
/** Create a new fibril for a new connection.
452
/** Create a new fibril for a new connection.
453
 *
453
 *
454
 * Creates new fibril for connection, fills in connection structures and inserts
454
 * Creates new fibril for connection, fills in connection structures and inserts
455
 * 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
456
 * particular fibrils.
456
 * particular fibrils.
457
 *
457
 *
458
 * @param in_phone_hash Identification of the incoming connection.
458
 * @param in_phone_hash Identification of the incoming connection.
459
 * @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.
460
 *          If callid is zero, the connection was opened by
460
 *          If callid is zero, the connection was opened by
461
 *          accepting the IPC_M_CONNECT_TO_ME call and this function
461
 *          accepting the IPC_M_CONNECT_TO_ME call and this function
462
 *          is called directly by the server.
462
 *          is called directly by the server.
463
 * @param call      Call data of the opening call.
463
 * @param call      Call data of the opening call.
464
 * @param cfibril   Fibril function that should be called upon opening the
464
 * @param cfibril   Fibril function that should be called upon opening the
465
 *          connection.
465
 *          connection.
466
 *
466
 *
467
 * @return      New fibril id or NULL on failure.
467
 * @return      New fibril id or NULL on failure.
468
 */
468
 */
469
fid_t async_new_connection(ipcarg_t in_phone_hash, ipc_callid_t callid,
469
fid_t async_new_connection(ipcarg_t in_phone_hash, ipc_callid_t callid,
470
    ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *))
470
    ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *))
471
{
471
{
472
    connection_t *conn;
472
    connection_t *conn;
473
    unsigned long key;
473
    unsigned long key;
474
 
474
 
475
    conn = malloc(sizeof(*conn));
475
    conn = malloc(sizeof(*conn));
476
    if (!conn) {
476
    if (!conn) {
477
        if (callid)
477
        if (callid)
478
            ipc_answer_fast(callid, ENOMEM, 0, 0);
478
            ipc_answer_fast(callid, ENOMEM, 0, 0);
479
        return NULL;
479
        return NULL;
480
    }
480
    }
481
    conn->in_phone_hash = in_phone_hash;
481
    conn->in_phone_hash = in_phone_hash;
482
    list_initialize(&conn->msg_queue);
482
    list_initialize(&conn->msg_queue);
483
    conn->callid = callid;
483
    conn->callid = callid;
484
    conn->close_callid = 0;
484
    conn->close_callid = 0;
485
    if (call)
485
    if (call)
486
        conn->call = *call;
486
        conn->call = *call;
487
    conn->wdata.active = 1; /* We will activate the fibril ASAP */
487
    conn->wdata.active = 1; /* We will activate the fibril ASAP */
488
    conn->cfibril = cfibril;
488
    conn->cfibril = cfibril;
489
 
489
 
490
    conn->wdata.fid = fibril_create(connection_fibril, conn);
490
    conn->wdata.fid = fibril_create(connection_fibril, conn);
491
    if (!conn->wdata.fid) {
491
    if (!conn->wdata.fid) {
492
        free(conn);
492
        free(conn);
493
        if (callid)
493
        if (callid)
494
            ipc_answer_fast(callid, ENOMEM, 0, 0);
494
            ipc_answer_fast(callid, ENOMEM, 0, 0);
495
        return NULL;
495
        return NULL;
496
    }
496
    }
497
    /* Add connection to the connection hash table */
497
    /* Add connection to the connection hash table */
498
    key = conn->in_phone_hash;
498
    key = conn->in_phone_hash;
499
    futex_down(&async_futex);
499
    futex_down(&async_futex);
500
    hash_table_insert(&conn_hash_table, &key, &conn->link);
500
    hash_table_insert(&conn_hash_table, &key, &conn->link);
501
    futex_up(&async_futex);
501
    futex_up(&async_futex);
502
 
502
 
503
    fibril_add_ready(conn->wdata.fid);
503
    fibril_add_ready(conn->wdata.fid);
504
 
504
 
505
    return conn->wdata.fid;
505
    return conn->wdata.fid;
506
}
506
}
507
 
507
 
508
/** Handle a call that was received.
508
/** Handle a call that was received.
509
 *
509
 *
510
 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
510
 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
511
 * Otherwise the call is routed to its connection fibril.
511
 * Otherwise the call is routed to its connection fibril.
512
 *
512
 *
513
 * @param callid    Hash of the incoming call.
513
 * @param callid    Hash of the incoming call.
514
 * @param call      Data of the incoming call.
514
 * @param call      Data of the incoming call.
515
 */
515
 */
516
static void handle_call(ipc_callid_t callid, ipc_call_t *call)
516
static void handle_call(ipc_callid_t callid, ipc_call_t *call)
517
{
517
{
518
    /* Unrouted call - do some default behaviour */
518
    /* Unrouted call - do some default behaviour */
519
    if ((callid & IPC_CALLID_NOTIFICATION)) {
519
    if ((callid & IPC_CALLID_NOTIFICATION)) {
520
        in_interrupt_handler = 1;
520
        in_interrupt_handler = 1;
521
        (*interrupt_received)(callid,call);
521
        (*interrupt_received)(callid, call);
522
        in_interrupt_handler = 0;
522
        in_interrupt_handler = 0;
523
        return;
523
        return;
524
    }      
524
    }      
525
 
525
 
526
    switch (IPC_GET_METHOD(*call)) {
526
    switch (IPC_GET_METHOD(*call)) {
527
    case IPC_M_CONNECT_ME_TO:
527
    case IPC_M_CONNECT_ME_TO:
528
        /* Open new connection with fibril etc. */
528
        /* Open new connection with fibril etc. */
529
        async_new_connection(IPC_GET_ARG3(*call), callid, call,
529
        async_new_connection(IPC_GET_ARG3(*call), callid, call,
530
            client_connection);
530
            client_connection);
531
        return;
531
        return;
532
    }
532
    }
533
 
533
 
534
    /* Try to route the call through the connection hash table */
534
    /* Try to route the call through the connection hash table */
535
    if (route_call(callid, call))
535
    if (route_call(callid, call))
536
        return;
536
        return;
537
 
537
 
538
    /* Unknown call from unknown phone - hang it up */
538
    /* Unknown call from unknown phone - hang it up */
539
    ipc_answer_fast(callid, EHANGUP, 0, 0);
539
    ipc_answer_fast(callid, EHANGUP, 0, 0);
540
}
540
}
541
 
541
 
542
/** Fire all timeouts that expired. */
542
/** Fire all timeouts that expired. */
543
static void handle_expired_timeouts(void)
543
static void handle_expired_timeouts(void)
544
{
544
{
545
    struct timeval tv;
545
    struct timeval tv;
546
    awaiter_t *waiter;
546
    awaiter_t *waiter;
547
    link_t *cur;
547
    link_t *cur;
548
 
548
 
549
    gettimeofday(&tv, NULL);
549
    gettimeofday(&tv, NULL);
550
    futex_down(&async_futex);
550
    futex_down(&async_futex);
551
 
551
 
552
    cur = timeout_list.next;
552
    cur = timeout_list.next;
553
    while (cur != &timeout_list) {
553
    while (cur != &timeout_list) {
554
        waiter = list_get_instance(cur, awaiter_t, link);
554
        waiter = list_get_instance(cur, awaiter_t, link);
555
        if (tv_gt(&waiter->expires, &tv))
555
        if (tv_gt(&waiter->expires, &tv))
556
            break;
556
            break;
557
        cur = cur->next;
557
        cur = cur->next;
558
        list_remove(&waiter->link);
558
        list_remove(&waiter->link);
559
        waiter->inlist = 0;
559
        waiter->inlist = 0;
560
        waiter->timedout = 1;
560
        waiter->timedout = 1;
561
        /*
561
        /*
562
         * Redundant condition?
562
         * Redundant condition?
563
         * The fibril should not be active when it gets here.
563
         * The fibril should not be active when it gets here.
564
         */
564
         */
565
        if (!waiter->active) {
565
        if (!waiter->active) {
566
            waiter->active = 1;
566
            waiter->active = 1;
567
            fibril_add_ready(waiter->fid);
567
            fibril_add_ready(waiter->fid);
568
        }
568
        }
569
    }
569
    }
570
 
570
 
571
    futex_up(&async_futex);
571
    futex_up(&async_futex);
572
}
572
}
573
 
573
 
574
/** Endless loop dispatching incoming calls and answers.
574
/** Endless loop dispatching incoming calls and answers.
575
 *
575
 *
576
 * @return      Never returns.
576
 * @return      Never returns.
577
 */
577
 */
578
static int async_manager_worker(void)
578
static int async_manager_worker(void)
579
{
579
{
580
    ipc_call_t call;
580
    ipc_call_t call;
581
    ipc_callid_t callid;
581
    ipc_callid_t callid;
582
    int timeout;
582
    int timeout;
583
    awaiter_t *waiter;
583
    awaiter_t *waiter;
584
    struct timeval tv;
584
    struct timeval tv;
585
 
585
 
586
    while (1) {
586
    while (1) {
587
        if (fibril_switch(FIBRIL_FROM_MANAGER)) {
587
        if (fibril_switch(FIBRIL_FROM_MANAGER)) {
588
            futex_up(&async_futex);
588
            futex_up(&async_futex);
589
            /*
589
            /*
590
             * async_futex is always held when entering a manager
590
             * async_futex is always held when entering a manager
591
             * fibril.
591
             * fibril.
592
             */
592
             */
593
            continue;
593
            continue;
594
        }
594
        }
595
        futex_down(&async_futex);
595
        futex_down(&async_futex);
596
        if (!list_empty(&timeout_list)) {
596
        if (!list_empty(&timeout_list)) {
597
            waiter = list_get_instance(timeout_list.next, awaiter_t,
597
            waiter = list_get_instance(timeout_list.next, awaiter_t,
598
                link);
598
                link);
599
            gettimeofday(&tv, NULL);
599
            gettimeofday(&tv, NULL);
600
            if (tv_gteq(&tv, &waiter->expires)) {
600
            if (tv_gteq(&tv, &waiter->expires)) {
601
                futex_up(&async_futex);
601
                futex_up(&async_futex);
602
                handle_expired_timeouts();
602
                handle_expired_timeouts();
603
                continue;
603
                continue;
604
            } else
604
            } else
605
                timeout = tv_sub(&waiter->expires, &tv);
605
                timeout = tv_sub(&waiter->expires, &tv);
606
        } else
606
        } else
607
            timeout = SYNCH_NO_TIMEOUT;
607
            timeout = SYNCH_NO_TIMEOUT;
608
        futex_up(&async_futex);
608
        futex_up(&async_futex);
609
 
609
 
610
        callid = ipc_wait_cycle(&call, timeout, SYNCH_FLAGS_NONE);
610
        callid = ipc_wait_cycle(&call, timeout, SYNCH_FLAGS_NONE);
611
 
611
 
612
        if (!callid) {
612
        if (!callid) {
613
            handle_expired_timeouts();
613
            handle_expired_timeouts();
614
            continue;
614
            continue;
615
        }
615
        }
616
 
616
 
617
        if (callid & IPC_CALLID_ANSWERED) {
617
        if (callid & IPC_CALLID_ANSWERED) {
618
            continue;
618
            continue;
619
        }
619
        }
620
 
620
 
621
        handle_call(callid, &call);
621
        handle_call(callid, &call);
622
    }
622
    }
623
   
623
   
624
    return 0;
624
    return 0;
625
}
625
}
626
 
626
 
627
/** Function to start async_manager as a standalone fibril.
627
/** Function to start async_manager as a standalone fibril.
628
 *
628
 *
629
 * When more kernel threads are used, one async manager should exist per thread.
629
 * When more kernel threads are used, one async manager should exist per thread.
630
 *
630
 *
631
 * @param arg       Unused.
631
 * @param arg       Unused.
632
 *
632
 *
633
 * @return      Never returns.
633
 * @return      Never returns.
634
 */
634
 */
635
static int async_manager_fibril(void *arg)
635
static int async_manager_fibril(void *arg)
636
{
636
{
637
    futex_up(&async_futex);
637
    futex_up(&async_futex);
638
    /*
638
    /*
639
     * async_futex is always locked when entering manager
639
     * async_futex is always locked when entering manager
640
     */
640
     */
641
    async_manager_worker();
641
    async_manager_worker();
642
   
642
   
643
    return 0;
643
    return 0;
644
}
644
}
645
 
645
 
646
/** Add one manager to manager list. */
646
/** Add one manager to manager list. */
647
void async_create_manager(void)
647
void async_create_manager(void)
648
{
648
{
649
    fid_t fid;
649
    fid_t fid;
650
 
650
 
651
    fid = fibril_create(async_manager_fibril, NULL);
651
    fid = fibril_create(async_manager_fibril, NULL);
652
    fibril_add_manager(fid);
652
    fibril_add_manager(fid);
653
}
653
}
654
 
654
 
655
/** Remove one manager from manager list */
655
/** Remove one manager from manager list */
656
void async_destroy_manager(void)
656
void async_destroy_manager(void)
657
{
657
{
658
    fibril_remove_manager();
658
    fibril_remove_manager();
659
}
659
}
660
 
660
 
661
/** Initialize the async framework.
661
/** Initialize the async framework.
662
 *
662
 *
663
 * @return      Zero on success or an error code.
663
 * @return      Zero on success or an error code.
664
 */
664
 */
665
int _async_init(void)
665
int _async_init(void)
666
{
666
{
667
    if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
667
    if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
668
        &conn_hash_table_ops)) {
668
        &conn_hash_table_ops)) {
669
        printf("%s: cannot create hash table\n", "async");
669
        printf("%s: cannot create hash table\n", "async");
670
        return ENOMEM;
670
        return ENOMEM;
671
    }
671
    }
672
   
672
   
673
    return 0;
673
    return 0;
674
}
674
}
675
 
675
 
676
/** Reply received callback.
676
/** Reply received callback.
677
 *
677
 *
678
 * This function is called whenever a reply for an asynchronous message sent out
678
 * This function is called whenever a reply for an asynchronous message sent out
679
 * by the asynchronous framework is received.
679
 * by the asynchronous framework is received.
680
 *
680
 *
681
 * Notify the fibril which is waiting for this message that it has arrived.
681
 * Notify the fibril which is waiting for this message that it has arrived.
682
 *
682
 *
683
 * @param private   Pointer to the asynchronous message record.
683
 * @param private   Pointer to the asynchronous message record.
684
 * @param retval    Value returned in the answer.
684
 * @param retval    Value returned in the answer.
685
 * @param data      Call data of the answer.
685
 * @param data      Call data of the answer.
686
 */
686
 */
687
static void reply_received(void *private, int retval, ipc_call_t *data)
687
static void reply_received(void *private, int retval, ipc_call_t *data)
688
{
688
{
689
    amsg_t *msg = (amsg_t *) private;
689
    amsg_t *msg = (amsg_t *) private;
690
 
690
 
691
    msg->retval = retval;
691
    msg->retval = retval;
692
 
692
 
693
    futex_down(&async_futex);
693
    futex_down(&async_futex);
694
    /* Copy data after futex_down, just in case the call was detached */
694
    /* Copy data after futex_down, just in case the call was detached */
695
    if (msg->dataptr)
695
    if (msg->dataptr)
696
        *msg->dataptr = *data;
696
        *msg->dataptr = *data;
697
 
697
 
698
    write_barrier();
698
    write_barrier();
699
    /* Remove message from timeout list */
699
    /* Remove message from timeout list */
700
    if (msg->wdata.inlist)
700
    if (msg->wdata.inlist)
701
        list_remove(&msg->wdata.link);
701
        list_remove(&msg->wdata.link);
702
    msg->done = 1;
702
    msg->done = 1;
703
    if (!msg->wdata.active) {
703
    if (!msg->wdata.active) {
704
        msg->wdata.active = 1;
704
        msg->wdata.active = 1;
705
        fibril_add_ready(msg->wdata.fid);
705
        fibril_add_ready(msg->wdata.fid);
706
    }
706
    }
707
    futex_up(&async_futex);
707
    futex_up(&async_futex);
708
}
708
}
709
 
709
 
710
/** Send message and return id of the sent message.
710
/** Send message and return id of the sent message.
711
 *
711
 *
712
 * The return value can be used as input for async_wait() to wait for
712
 * The return value can be used as input for async_wait() to wait for
713
 * completion.
713
 * completion.
714
 *
714
 *
715
 * @param phoneid   Handle of the phone that will be used for the send.
715
 * @param phoneid   Handle of the phone that will be used for the send.
716
 * @param method    Service-defined method.
716
 * @param method    Service-defined method.
717
 * @param arg1      Service-defined payload argument.
717
 * @param arg1      Service-defined payload argument.
718
 * @param arg2      Service-defined payload argument.
718
 * @param arg2      Service-defined payload argument.
719
 * @param dataptr   If non-NULL, storage where the reply data will be
719
 * @param dataptr   If non-NULL, storage where the reply data will be
720
 *          stored.
720
 *          stored.
721
 *
721
 *
722
 * @return      Hash of the sent message.
722
 * @return      Hash of the sent message.
723
 */
723
 */
724
aid_t async_send_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
724
aid_t async_send_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
725
    ipc_call_t *dataptr)
725
    ipc_call_t *dataptr)
726
{
726
{
727
    amsg_t *msg;
727
    amsg_t *msg;
728
 
728
 
729
    if (in_interrupt_handler) {
729
    if (in_interrupt_handler) {
730
        printf("Cannot send asynchronous request in interrupt "
730
        printf("Cannot send asynchronous request in interrupt "
731
            "handler.\n");
731
            "handler.\n");
732
        _exit(1);
732
        _exit(1);
733
    }
733
    }
734
 
734
 
735
    msg = malloc(sizeof(*msg));
735
    msg = malloc(sizeof(*msg));
736
    msg->done = 0;
736
    msg->done = 0;
737
    msg->dataptr = dataptr;
737
    msg->dataptr = dataptr;
738
 
738
 
739
    /* We may sleep in the next method, but it will use its own mechanism */
739
    /* We may sleep in the next method, but it will use its own mechanism */
740
    msg->wdata.active = 1;
740
    msg->wdata.active = 1;
741
               
741
               
742
    ipc_call_async_2(phoneid, method, arg1, arg2, msg, reply_received, 1);
742
    ipc_call_async_2(phoneid, method, arg1, arg2, msg, reply_received, 1);
743
 
743
 
744
    return (aid_t) msg;
744
    return (aid_t) msg;
745
}
745
}
746
 
746
 
747
/** Send message and return id of the sent message
747
/** Send message and return id of the sent message
748
 *
748
 *
749
 * The return value can be used as input for async_wait() to wait for
749
 * The return value can be used as input for async_wait() to wait for
750
 * completion.
750
 * completion.
751
 *
751
 *
752
 * @param phoneid   Handle of the phone that will be used for the send.
752
 * @param phoneid   Handle of the phone that will be used for the send.
753
 * @param method    Service-defined method.
753
 * @param method    Service-defined method.
754
 * @param arg1      Service-defined payload argument.
754
 * @param arg1      Service-defined payload argument.
755
 * @param arg2      Service-defined payload argument.
755
 * @param arg2      Service-defined payload argument.
756
 * @param arg3      Service-defined payload argument.
756
 * @param arg3      Service-defined payload argument.
757
 * @param dataptr   If non-NULL, storage where the reply data will be
757
 * @param dataptr   If non-NULL, storage where the reply data will be
758
 *          stored.
758
 *          stored.
759
 *
759
 *
760
 * @return      Hash of the sent message.
760
 * @return      Hash of the sent message.
761
 */
761
 */
762
aid_t async_send_3(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
762
aid_t async_send_3(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
763
    ipcarg_t arg3, ipc_call_t *dataptr)
763
    ipcarg_t arg3, ipc_call_t *dataptr)
764
{
764
{
765
    amsg_t *msg;
765
    amsg_t *msg;
766
 
766
 
767
    if (in_interrupt_handler) {
767
    if (in_interrupt_handler) {
768
        printf("Cannot send asynchronous request in interrupt "
768
        printf("Cannot send asynchronous request in interrupt "
769
            "handler.\n");
769
            "handler.\n");
770
        _exit(1);
770
        _exit(1);
771
    }
771
    }
772
 
772
 
773
    msg = malloc(sizeof(*msg));
773
    msg = malloc(sizeof(*msg));
774
    msg->done = 0;
774
    msg->done = 0;
775
    msg->dataptr = dataptr;
775
    msg->dataptr = dataptr;
776
 
776
 
777
    /* We may sleep in next method, but it will use its own mechanism */
777
    /* We may sleep in next method, but it will use its own mechanism */
778
    msg->wdata.active = 1;
778
    msg->wdata.active = 1;
779
               
779
               
780
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, msg, reply_received,
780
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, msg, reply_received,
781
        1);
781
        1);
782
 
782
 
783
    return (aid_t) msg;
783
    return (aid_t) msg;
784
}
784
}
785
 
785
 
786
/** Wait for a message sent by the async framework.
786
/** Wait for a message sent by the async framework.
787
 *
787
 *
788
 * @param amsgid    Hash of the message to wait for.
788
 * @param amsgid    Hash of the message to wait for.
789
 * @param retval    Pointer to storage where the retval of the answer will
789
 * @param retval    Pointer to storage where the retval of the answer will
790
 *          be stored.
790
 *          be stored.
791
 */
791
 */
792
void async_wait_for(aid_t amsgid, ipcarg_t *retval)
792
void async_wait_for(aid_t amsgid, ipcarg_t *retval)
793
{
793
{
794
    amsg_t *msg = (amsg_t *) amsgid;
794
    amsg_t *msg = (amsg_t *) amsgid;
795
 
795
 
796
    futex_down(&async_futex);
796
    futex_down(&async_futex);
797
    if (msg->done) {
797
    if (msg->done) {
798
        futex_up(&async_futex);
798
        futex_up(&async_futex);
799
        goto done;
799
        goto done;
800
    }
800
    }
801
 
801
 
802
    msg->wdata.fid = fibril_get_id();
802
    msg->wdata.fid = fibril_get_id();
803
    msg->wdata.active = 0;
803
    msg->wdata.active = 0;
804
    msg->wdata.inlist = 0;
804
    msg->wdata.inlist = 0;
805
    /* Leave the async_futex locked when entering this function */
805
    /* Leave the async_futex locked when entering this function */
806
    fibril_switch(FIBRIL_TO_MANAGER);
806
    fibril_switch(FIBRIL_TO_MANAGER);
807
    /* futex is up automatically after fibril_switch...*/
807
    /* futex is up automatically after fibril_switch...*/
808
done:
808
done:
809
    if (retval)
809
    if (retval)
810
        *retval = msg->retval;
810
        *retval = msg->retval;
811
    free(msg);
811
    free(msg);
812
}
812
}
813
 
813
 
814
/** Wait for a message sent by the async framework, timeout variant.
814
/** Wait for a message sent by the async framework, timeout variant.
815
 *
815
 *
816
 * @param amsgid    Hash of the message to wait for.
816
 * @param amsgid    Hash of the message to wait for.
817
 * @param retval    Pointer to storage where the retval of the answer will
817
 * @param retval    Pointer to storage where the retval of the answer will
818
 *          be stored.
818
 *          be stored.
819
 * @param timeout   Timeout in microseconds.
819
 * @param timeout   Timeout in microseconds.
820
 *
820
 *
821
 * @return      Zero on success, ETIMEOUT if the timeout has expired.
821
 * @return      Zero on success, ETIMEOUT if the timeout has expired.
822
 */
822
 */
823
int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout)
823
int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout)
824
{
824
{
825
    amsg_t *msg = (amsg_t *) amsgid;
825
    amsg_t *msg = (amsg_t *) amsgid;
826
 
826
 
827
    /* TODO: Let it go through the event read at least once */
827
    /* TODO: Let it go through the event read at least once */
828
    if (timeout < 0)
828
    if (timeout < 0)
829
        return ETIMEOUT;
829
        return ETIMEOUT;
830
 
830
 
831
    futex_down(&async_futex);
831
    futex_down(&async_futex);
832
    if (msg->done) {
832
    if (msg->done) {
833
        futex_up(&async_futex);
833
        futex_up(&async_futex);
834
        goto done;
834
        goto done;
835
    }
835
    }
836
 
836
 
837
    gettimeofday(&msg->wdata.expires, NULL);
837
    gettimeofday(&msg->wdata.expires, NULL);
838
    tv_add(&msg->wdata.expires, timeout);
838
    tv_add(&msg->wdata.expires, timeout);
839
 
839
 
840
    msg->wdata.fid = fibril_get_id();
840
    msg->wdata.fid = fibril_get_id();
841
    msg->wdata.active = 0;
841
    msg->wdata.active = 0;
842
    insert_timeout(&msg->wdata);
842
    insert_timeout(&msg->wdata);
843
 
843
 
844
    /* Leave the async_futex locked when entering this function */
844
    /* Leave the async_futex locked when entering this function */
845
    fibril_switch(FIBRIL_TO_MANAGER);
845
    fibril_switch(FIBRIL_TO_MANAGER);
846
    /* futex is up automatically after fibril_switch...*/
846
    /* futex is up automatically after fibril_switch...*/
847
 
847
 
848
    if (!msg->done)
848
    if (!msg->done)
849
        return ETIMEOUT;
849
        return ETIMEOUT;
850
 
850
 
851
done:
851
done:
852
    if (retval)
852
    if (retval)
853
        *retval = msg->retval;
853
        *retval = msg->retval;
854
    free(msg);
854
    free(msg);
855
 
855
 
856
    return 0;
856
    return 0;
857
}
857
}
858
 
858
 
859
/** Wait for specified time.
859
/** Wait for specified time.
860
 *
860
 *
861
 * The current fibril is suspended but the thread continues to execute.
861
 * The current fibril is suspended but the thread continues to execute.
862
 *
862
 *
863
 * @param timeout   Duration of the wait in microseconds.
863
 * @param timeout   Duration of the wait in microseconds.
864
 */
864
 */
865
void async_usleep(suseconds_t timeout)
865
void async_usleep(suseconds_t timeout)
866
{
866
{
867
    amsg_t *msg;
867
    amsg_t *msg;
868
   
868
   
869
    if (in_interrupt_handler) {
869
    if (in_interrupt_handler) {
870
        printf("Cannot call async_usleep in interrupt handler.\n");
870
        printf("Cannot call async_usleep in interrupt handler.\n");
871
        _exit(1);
871
        _exit(1);
872
    }
872
    }
873
 
873
 
874
    msg = malloc(sizeof(*msg));
874
    msg = malloc(sizeof(*msg));
875
    if (!msg)
875
    if (!msg)
876
        return;
876
        return;
877
 
877
 
878
    msg->wdata.fid = fibril_get_id();
878
    msg->wdata.fid = fibril_get_id();
879
    msg->wdata.active = 0;
879
    msg->wdata.active = 0;
880
 
880
 
881
    gettimeofday(&msg->wdata.expires, NULL);
881
    gettimeofday(&msg->wdata.expires, NULL);
882
    tv_add(&msg->wdata.expires, timeout);
882
    tv_add(&msg->wdata.expires, timeout);
883
 
883
 
884
    futex_down(&async_futex);
884
    futex_down(&async_futex);
885
    insert_timeout(&msg->wdata);
885
    insert_timeout(&msg->wdata);
886
    /* Leave the async_futex locked when entering this function */
886
    /* Leave the async_futex locked when entering this function */
887
    fibril_switch(FIBRIL_TO_MANAGER);
887
    fibril_switch(FIBRIL_TO_MANAGER);
888
    /* futex is up automatically after fibril_switch()...*/
888
    /* futex is up automatically after fibril_switch()...*/
889
    free(msg);
889
    free(msg);
890
}
890
}
891
 
891
 
892
/** Setter for client_connection function pointer.
892
/** Setter for client_connection function pointer.
893
 *
893
 *
894
 * @param conn      Function that will implement a new connection fibril.
894
 * @param conn      Function that will implement a new connection fibril.
895
 */
895
 */
896
void async_set_client_connection(async_client_conn_t conn)
896
void async_set_client_connection(async_client_conn_t conn)
897
{
897
{
898
    client_connection = conn;
898
    client_connection = conn;
899
}
899
}
900
 
900
 
901
/** Setter for interrupt_received function pointer.
901
/** Setter for interrupt_received function pointer.
902
 *
902
 *
903
 * @param conn      Function that will implement a new interrupt
903
 * @param conn      Function that will implement a new interrupt
904
 *          notification fibril.
904
 *          notification fibril.
905
 */
905
 */
906
void async_set_interrupt_received(async_client_conn_t conn)
906
void async_set_interrupt_received(async_client_conn_t conn)
907
{
907
{
908
    interrupt_received = conn;
908
    interrupt_received = conn;
909
}
909
}
910
 
910
 
911
/* Primitive functions for simple communication */
911
/* Primitive functions for simple communication */
912
void async_msg_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
912
void async_msg_3(int phoneid, ipcarg_t method, ipcarg_t arg1,
913
         ipcarg_t arg2, ipcarg_t arg3)
913
         ipcarg_t arg2, ipcarg_t arg3)
914
{
914
{
915
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, NULL, NULL,
915
    ipc_call_async_3(phoneid, method, arg1, arg2, arg3, NULL, NULL,
916
        !in_interrupt_handler);
916
        !in_interrupt_handler);
917
}
917
}
918
 
918
 
919
void async_msg_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2)
919
void async_msg_2(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2)
920
{
920
{
921
    ipc_call_async_2(phoneid, method, arg1, arg2, NULL, NULL,
921
    ipc_call_async_2(phoneid, method, arg1, arg2, NULL, NULL,
922
        !in_interrupt_handler);
922
        !in_interrupt_handler);
923
}
923
}
924
 
924
 
925
/** @}
925
/** @}
926
 */
926
 */
927
 
927
 
928
 
928