Subversion Repositories HelenOS

Rev

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

Rev 4381 Rev 4407
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 to provide a facility for writing programs which
38
 * The aim of this library is to provide a facility for writing programs which
39
 * utilize the asynchronous nature of HelenOS IPC, yet using a normal way of
39
 * utilize the asynchronous nature of HelenOS IPC, yet using a normal way of
40
 * programming.
40
 * programming.
41
 *
41
 *
42
 * You should be able to write very simple multithreaded programs, the async
42
 * You should be able to write very simple multithreaded programs, the async
43
 * framework will automatically take care of most synchronization problems.
43
 * framework will automatically take care of most synchronization problems.
44
 *
44
 *
45
 * Default semantics:
45
 * Default semantics:
46
 * - async_send_*(): Send asynchronously. If the kernel refuses to send
46
 * - async_send_*(): Send asynchronously. If the kernel refuses to send
47
 *                   more messages, [ try to get responses from kernel, if
47
 *                   more messages, [ try to get responses from kernel, if
48
 *                   nothing found, might try synchronous ]
48
 *                   nothing found, might try synchronous ]
49
 *
49
 *
50
 * Example of use (pseudo C):
50
 * Example of use (pseudo C):
51
 *
51
 *
52
 * 1) Multithreaded client application
52
 * 1) Multithreaded client application
53
 *
53
 *
54
 *   fibril_create(fibril1, ...);
54
 *   fibril_create(fibril1, ...);
55
 *   fibril_create(fibril2, ...);
55
 *   fibril_create(fibril2, ...);
56
 *   ...
56
 *   ...
57
 *
57
 *
58
 *   int fibril1(void *arg)
58
 *   int fibril1(void *arg)
59
 *   {
59
 *   {
60
 *     conn = ipc_connect_me_to();
60
 *     conn = ipc_connect_me_to();
61
 *     c1 = async_send(conn);
61
 *     c1 = async_send(conn);
62
 *     c2 = async_send(conn);
62
 *     c2 = async_send(conn);
63
 *     async_wait_for(c1);
63
 *     async_wait_for(c1);
64
 *     async_wait_for(c2);
64
 *     async_wait_for(c2);
65
 *     ...
65
 *     ...
66
 *   }
66
 *   }
67
 *
67
 *
68
 *
68
 *
69
 * 2) Multithreaded server application
69
 * 2) Multithreaded server application
70
 *
70
 *
71
 *   main()
71
 *   main()
72
 *   {
72
 *   {
73
 *     async_manager();
73
 *     async_manager();
74
 *   }
74
 *   }
75
 *
75
 *
76
 *   my_client_connection(icallid, *icall)
76
 *   my_client_connection(icallid, *icall)
77
 *   {
77
 *   {
78
 *     if (want_refuse) {
78
 *     if (want_refuse) {
79
 *       ipc_answer_0(icallid, ELIMIT);
79
 *       ipc_answer_0(icallid, ELIMIT);
80
 *       return;
80
 *       return;
81
 *     }
81
 *     }
82
 *     ipc_answer_0(icallid, EOK);
82
 *     ipc_answer_0(icallid, EOK);
83
 *
83
 *
84
 *     callid = async_get_call(&call);
84
 *     callid = async_get_call(&call);
85
 *     handle_call(callid, call);
85
 *     handle_call(callid, call);
86
 *     ipc_answer_2(callid, 1, 2, 3);
86
 *     ipc_answer_2(callid, 1, 2, 3);
87
 *
87
 *
88
 *     callid = async_get_call(&call);
88
 *     callid = async_get_call(&call);
89
 *     ...
89
 *     ...
90
 *   }
90
 *   }
91
 *
91
 *
92
 */
92
 */
93
 
93
 
94
#include <futex.h>
94
#include <futex.h>
95
#include <async.h>
95
#include <async.h>
96
#include <fibril.h>
96
#include <fibril.h>
97
#include <stdio.h>
97
#include <stdio.h>
98
#include <libadt/hash_table.h>
98
#include <libadt/hash_table.h>
99
#include <libadt/list.h>
99
#include <libadt/list.h>
100
#include <ipc/ipc.h>
100
#include <ipc/ipc.h>
101
#include <assert.h>
101
#include <assert.h>
102
#include <errno.h>
102
#include <errno.h>
103
#include <sys/time.h>
103
#include <sys/time.h>
104
#include <arch/barrier.h>
104
#include <arch/barrier.h>
105
#include <bool.h>
105
#include <bool.h>
106
 
106
 
107
atomic_t async_futex = FUTEX_INITIALIZER;
107
atomic_t async_futex = FUTEX_INITIALIZER;
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
   
113
   
114
    /** If true, this struct is in the timeout list. */
114
    /** If true, this struct is in the timeout list. */
115
    bool inlist;
115
    bool inlist;
116
   
116
   
117
    /** Timeout list link. */
117
    /** Timeout list link. */
118
    link_t link;
118
    link_t link;
119
   
119
   
120
    /** Identification of and link to the waiting fibril. */
120
    /** Identification of and link to the waiting fibril. */
121
    fid_t fid;
121
    fid_t fid;
122
   
122
   
123
    /** If true, this fibril is currently active. */
123
    /** If true, this fibril is currently active. */
124
    bool active;
124
    bool active;
125
   
125
   
126
    /** If true, we have timed out. */
126
    /** If true, we have timed out. */
127
    bool timedout;
127
    bool timedout;
128
} awaiter_t;
128
} awaiter_t;
129
 
129
 
130
typedef struct {
130
typedef struct {
131
    awaiter_t wdata;
131
    awaiter_t wdata;
132
   
132
   
133
    /** If reply was received. */
133
    /** If reply was received. */
134
    bool done;
134
    bool done;
135
   
135
   
136
    /** Pointer to where the answer data is stored. */
136
    /** Pointer to where the answer data is stored. */
137
    ipc_call_t *dataptr;
137
    ipc_call_t *dataptr;
138
   
138
   
139
    ipcarg_t retval;
139
    ipcarg_t retval;
140
} amsg_t;
140
} amsg_t;
141
 
141
 
142
/**
142
/**
143
 * Structures of this type are used to group information about a call and a
143
 * Structures of this type are used to group information about a call and a
144
 * message queue link.
144
 * message queue link.
145
 */
145
 */
146
typedef struct {
146
typedef struct {
147
    link_t link;
147
    link_t link;
148
    ipc_callid_t callid;
148
    ipc_callid_t callid;
149
    ipc_call_t call;
149
    ipc_call_t call;
150
} msg_t;
150
} msg_t;
151
 
151
 
152
typedef struct {
152
typedef struct {
153
    awaiter_t wdata;
153
    awaiter_t wdata;
154
   
154
   
155
    /** Hash table link. */
155
    /** Hash table link. */
156
    link_t link;
156
    link_t link;
157
   
157
   
158
    /** Incoming phone hash. */
158
    /** Incoming phone hash. */
159
    ipcarg_t in_phone_hash;
159
    ipcarg_t in_phone_hash;
160
   
160
   
161
    /** Messages that should be delivered to this fibril. */
161
    /** Messages that should be delivered to this fibril. */
162
    link_t msg_queue;
162
    link_t msg_queue;
163
   
163
   
164
    /** Identification of the opening call. */
164
    /** Identification of the opening call. */
165
    ipc_callid_t callid;
165
    ipc_callid_t callid;
166
    /** Call data of the opening call. */
166
    /** Call data of the opening call. */
167
    ipc_call_t call;
167
    ipc_call_t call;
168
   
168
   
169
    /** Identification of the closing call. */
169
    /** Identification of the closing call. */
170
    ipc_callid_t close_callid;
170
    ipc_callid_t close_callid;
171
   
171
   
172
    /** Fibril function that will be used to handle the connection. */
172
    /** Fibril function that will be used to handle the connection. */
173
    void (*cfibril)(ipc_callid_t, ipc_call_t *);
173
    void (*cfibril)(ipc_callid_t, ipc_call_t *);
174
} connection_t;
174
} connection_t;
175
 
175
 
176
/** Identifier of the incoming connection handled by the current fibril. */
176
/** Identifier of the incoming connection handled by the current fibril. */
177
__thread connection_t *FIBRIL_connection;
177
__thread connection_t *FIBRIL_connection;
178
 
178
 
179
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call);
179
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call);
180
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call);
180
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call);
181
 
181
 
182
/**
182
/**
183
 * Pointer to a fibril function that will be used to handle connections.
183
 * Pointer to a fibril function that will be used to handle connections.
184
 */
184
 */
185
static async_client_conn_t client_connection = default_client_connection;
185
static async_client_conn_t client_connection = default_client_connection;
186
 
186
 
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
 
193
 
194
static hash_table_t conn_hash_table;
194
static hash_table_t conn_hash_table;
195
static LIST_INITIALIZE(timeout_list);
195
static LIST_INITIALIZE(timeout_list);
196
 
196
 
197
 
197
 
198
#define CONN_HASH_TABLE_CHAINS  32
198
#define CONN_HASH_TABLE_CHAINS  32
199
 
199
 
200
/** Compute hash into the connection hash table based on the source phone hash.
200
/** Compute hash into the connection hash table based on the source phone hash.
201
 *
201
 *
202
 * @param key Pointer to source phone hash.
202
 * @param key Pointer to source phone hash.
203
 *
203
 *
204
 * @return Index into the connection hash table.
204
 * @return Index into the connection hash table.
205
 *
205
 *
206
 */
206
 */
207
static hash_index_t conn_hash(unsigned long *key)
207
static hash_index_t conn_hash(unsigned long *key)
208
{
208
{
209
    assert(key);
209
    assert(key);
210
    return (((*key) >> 4) % CONN_HASH_TABLE_CHAINS);
210
    return (((*key) >> 4) % CONN_HASH_TABLE_CHAINS);
211
}
211
}
212
 
212
 
213
/** Compare hash table item with a key.
213
/** Compare hash table item with a key.
214
 *
214
 *
215
 * @param key  Array containing the source phone hash as the only item.
215
 * @param key  Array containing the source phone hash as the only item.
216
 * @param keys Expected 1 but ignored.
216
 * @param keys Expected 1 but ignored.
217
 * @param item Connection hash table item.
217
 * @param item Connection hash table item.
218
 *
218
 *
219
 * @return True on match, false otherwise.
219
 * @return True on match, false otherwise.
220
 *
220
 *
221
 */
221
 */
222
static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
222
static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
223
{
223
{
224
    connection_t *hs = hash_table_get_instance(item, connection_t, link);
224
    connection_t *hs = hash_table_get_instance(item, connection_t, link);
225
    return (key[0] == hs->in_phone_hash);
225
    return (key[0] == hs->in_phone_hash);
226
}
226
}
227
 
227
 
228
/** Connection hash table removal callback function.
228
/** Connection hash table removal callback function.
229
 *
229
 *
230
 * This function is called whenever a connection is removed from the connection
230
 * This function is called whenever a connection is removed from the connection
231
 * hash table.
231
 * hash table.
232
 *
232
 *
233
 * @param item Connection hash table item being removed.
233
 * @param item Connection hash table item being removed.
234
 *
234
 *
235
 */
235
 */
236
static void conn_remove(link_t *item)
236
static void conn_remove(link_t *item)
237
{
237
{
238
    free(hash_table_get_instance(item, connection_t, link));
238
    free(hash_table_get_instance(item, connection_t, link));
239
}
239
}
240
 
240
 
241
 
241
 
242
/** Operations for the connection hash table. */
242
/** Operations for the connection hash table. */
243
static hash_table_operations_t conn_hash_table_ops = {
243
static hash_table_operations_t conn_hash_table_ops = {
244
    .hash = conn_hash,
244
    .hash = conn_hash,
245
    .compare = conn_compare,
245
    .compare = conn_compare,
246
    .remove_callback = conn_remove
246
    .remove_callback = conn_remove
247
};
247
};
248
 
248
 
249
/** Sort in current fibril's timeout request.
249
/** Sort in current fibril's timeout request.
250
 *
250
 *
251
 * @param wd Wait data of the current fibril.
251
 * @param wd Wait data of the current fibril.
252
 *
252
 *
253
 */
253
 */
254
static void insert_timeout(awaiter_t *wd)
254
static void insert_timeout(awaiter_t *wd)
255
{
255
{
256
    wd->timedout = false;
256
    wd->timedout = false;
257
    wd->inlist = true;
257
    wd->inlist = true;
258
   
258
   
259
    link_t *tmp = timeout_list.next;
259
    link_t *tmp = timeout_list.next;
260
    while (tmp != &timeout_list) {
260
    while (tmp != &timeout_list) {
261
        awaiter_t *cur = list_get_instance(tmp, awaiter_t, link);
261
        awaiter_t *cur = list_get_instance(tmp, awaiter_t, link);
262
       
262
       
263
        if (tv_gteq(&cur->expires, &wd->expires))
263
        if (tv_gteq(&cur->expires, &wd->expires))
264
            break;
264
            break;
265
       
265
       
266
        tmp = tmp->next;
266
        tmp = tmp->next;
267
    }
267
    }
268
   
268
   
269
    list_append(&wd->link, tmp);
269
    list_append(&wd->link, tmp);
270
}
270
}
271
 
271
 
272
/** Try to route a call to an appropriate connection fibril.
272
/** Try to route a call to an appropriate connection fibril.
273
 *
273
 *
274
 * If the proper connection fibril is found, a message with the call is added to
274
 * If the proper connection fibril is found, a message with the call is added to
275
 * its message queue. If the fibril was not active, it is activated and all
275
 * its message queue. If the fibril was not active, it is activated and all
276
 * timeouts are unregistered.
276
 * timeouts are unregistered.
277
 *
277
 *
278
 * @param callid Hash of the incoming call.
278
 * @param callid Hash of the incoming call.
279
 * @param call   Data of the incoming call.
279
 * @param call   Data of the incoming call.
280
 *
280
 *
281
 * @return False if the call doesn't match any connection.
281
 * @return False if the call doesn't match any connection.
282
 *         True if the call was passed to the respective connection fibril.
282
 *         True if the call was passed to the respective connection fibril.
283
 *
283
 *
284
 */
284
 */
285
static bool route_call(ipc_callid_t callid, ipc_call_t *call)
285
static bool route_call(ipc_callid_t callid, ipc_call_t *call)
286
{
286
{
287
    futex_down(&async_futex);
287
    futex_down(&async_futex);
288
   
288
   
289
    unsigned long key = call->in_phone_hash;
289
    unsigned long key = call->in_phone_hash;
290
    link_t *hlp = hash_table_find(&conn_hash_table, &key);
290
    link_t *hlp = hash_table_find(&conn_hash_table, &key);
291
   
291
   
292
    if (!hlp) {
292
    if (!hlp) {
293
        futex_up(&async_futex);
293
        futex_up(&async_futex);
294
        return false;
294
        return false;
295
    }
295
    }
296
   
296
   
297
    connection_t *conn = hash_table_get_instance(hlp, connection_t, link);
297
    connection_t *conn = hash_table_get_instance(hlp, connection_t, link);
298
   
298
   
299
    msg_t *msg = malloc(sizeof(*msg));
299
    msg_t *msg = malloc(sizeof(*msg));
300
    if (!msg) {
300
    if (!msg) {
301
        futex_up(&async_futex);
301
        futex_up(&async_futex);
302
        return false;
302
        return false;
303
    }
303
    }
304
   
304
   
305
    msg->callid = callid;
305
    msg->callid = callid;
306
    msg->call = *call;
306
    msg->call = *call;
307
    list_append(&msg->link, &conn->msg_queue);
307
    list_append(&msg->link, &conn->msg_queue);
308
   
308
   
309
    if (IPC_GET_METHOD(*call) == IPC_M_PHONE_HUNGUP)
309
    if (IPC_GET_METHOD(*call) == IPC_M_PHONE_HUNGUP)
310
        conn->close_callid = callid;
310
        conn->close_callid = callid;
311
   
311
   
312
    /* If the connection fibril is waiting for an event, activate it */
312
    /* If the connection fibril is waiting for an event, activate it */
313
    if (!conn->wdata.active) {
313
    if (!conn->wdata.active) {
314
       
314
       
315
        /* If in timeout list, remove it */
315
        /* If in timeout list, remove it */
316
        if (conn->wdata.inlist) {
316
        if (conn->wdata.inlist) {
317
            conn->wdata.inlist = false;
317
            conn->wdata.inlist = false;
318
            list_remove(&conn->wdata.link);
318
            list_remove(&conn->wdata.link);
319
        }
319
        }
320
       
320
       
321
        conn->wdata.active = true;
321
        conn->wdata.active = true;
322
        fibril_add_ready(conn->wdata.fid);
322
        fibril_add_ready(conn->wdata.fid);
323
    }
323
    }
324
   
324
   
325
    futex_up(&async_futex);
325
    futex_up(&async_futex);
326
    return true;
326
    return true;
327
}
327
}
328
 
328
 
329
/** Notification fibril.
329
/** Notification fibril.
330
 *
330
 *
331
 * When a notification arrives, a fibril with this implementing function is
331
 * When a notification arrives, a fibril with this implementing function is
332
 * created. It calls interrupt_received() and does the final cleanup.
332
 * created. It calls interrupt_received() and does the final cleanup.
333
 *
333
 *
334
 * @param arg Message structure pointer.
334
 * @param arg Message structure pointer.
335
 *
335
 *
336
 * @return Always zero.
336
 * @return Always zero.
337
 *
337
 *
338
 */
338
 */
339
static int notification_fibril(void *arg)
339
static int notification_fibril(void *arg)
340
{
340
{
341
    msg_t *msg = (msg_t *) arg;
341
    msg_t *msg = (msg_t *) arg;
342
    interrupt_received(msg->callid, &msg->call);
342
    interrupt_received(msg->callid, &msg->call);
343
   
343
   
344
    free(msg);
344
    free(msg);
345
    return 0;
345
    return 0;
346
}
346
}
347
 
347
 
348
/** Process interrupt notification.
348
/** Process interrupt notification.
349
 *
349
 *
350
 * A new fibril is created which would process the notification.
350
 * A new fibril is created which would process the notification.
351
 *
351
 *
352
 * @param callid Hash of the incoming call.
352
 * @param callid Hash of the incoming call.
353
 * @param call   Data of the incoming call.
353
 * @param call   Data of the incoming call.
354
 *
354
 *
355
 * @return False if an error occured.
355
 * @return False if an error occured.
356
 *         True if the call was passed to the notification fibril.
356
 *         True if the call was passed to the notification fibril.
357
 *
357
 *
358
 */
358
 */
359
static bool process_notification(ipc_callid_t callid, ipc_call_t *call)
359
static bool process_notification(ipc_callid_t callid, ipc_call_t *call)
360
{
360
{
361
    futex_down(&async_futex);
361
    futex_down(&async_futex);
362
   
362
   
363
    msg_t *msg = malloc(sizeof(*msg));
363
    msg_t *msg = malloc(sizeof(*msg));
364
    if (!msg) {
364
    if (!msg) {
365
        futex_up(&async_futex);
365
        futex_up(&async_futex);
366
        return false;
366
        return false;
367
    }
367
    }
368
   
368
   
369
    msg->callid = callid;
369
    msg->callid = callid;
370
    msg->call = *call;
370
    msg->call = *call;
371
   
371
   
372
    fid_t fid = fibril_create(notification_fibril, msg);
372
    fid_t fid = fibril_create(notification_fibril, msg);
373
    fibril_add_ready(fid);
373
    fibril_add_ready(fid);
374
   
374
   
375
    futex_up(&async_futex);
375
    futex_up(&async_futex);
376
    return true;
376
    return true;
377
}
377
}
378
 
378
 
379
/** Return new incoming message for the current (fibril-local) connection.
379
/** Return new incoming message for the current (fibril-local) connection.
380
 *
380
 *
381
 * @param call  Storage where the incoming call data will be stored.
381
 * @param call  Storage where the incoming call data will be stored.
382
 * @param usecs Timeout in microseconds. Zero denotes no timeout.
382
 * @param usecs Timeout in microseconds. Zero denotes no timeout.
383
 *
383
 *
384
 * @return If no timeout was specified, then a hash of the
384
 * @return If no timeout was specified, then a hash of the
385
 *         incoming call is returned. If a timeout is specified,
385
 *         incoming call is returned. If a timeout is specified,
386
 *         then a hash of the incoming call is returned unless
386
 *         then a hash of the incoming call is returned unless
387
 *         the timeout expires prior to receiving a message. In
387
 *         the timeout expires prior to receiving a message. In
388
 *         that case zero is returned.
388
 *         that case zero is returned.
389
 *
389
 *
390
 */
390
 */
391
ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
391
ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
392
{
392
{
393
    assert(FIBRIL_connection);
393
    assert(FIBRIL_connection);
394
   
394
   
395
    /* Why doing this?
395
    /* Why doing this?
396
     * GCC 4.1.0 coughs on FIBRIL_connection-> dereference.
396
     * GCC 4.1.0 coughs on FIBRIL_connection-> dereference.
397
     * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
397
     * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
398
     *           I would never expect to find so many errors in
398
     *           I would never expect to find so many errors in
399
     *           a compiler.
399
     *           a compiler.
400
     */
400
     */
401
    connection_t *conn = FIBRIL_connection;
401
    connection_t *conn = FIBRIL_connection;
402
   
402
   
403
    futex_down(&async_futex);
403
    futex_down(&async_futex);
404
   
404
   
405
    if (usecs) {
405
    if (usecs) {
406
        gettimeofday(&conn->wdata.expires, NULL);
406
        gettimeofday(&conn->wdata.expires, NULL);
407
        tv_add(&conn->wdata.expires, usecs);
407
        tv_add(&conn->wdata.expires, usecs);
408
    } else
408
    } else
409
        conn->wdata.inlist = false;
409
        conn->wdata.inlist = false;
410
   
410
   
411
    /* If nothing in queue, wait until something arrives */
411
    /* If nothing in queue, wait until something arrives */
412
    while (list_empty(&conn->msg_queue)) {
412
    while (list_empty(&conn->msg_queue)) {
413
        if (usecs)
413
        if (usecs)
414
            insert_timeout(&conn->wdata);
414
            insert_timeout(&conn->wdata);
415
       
415
       
416
        conn->wdata.active = false;
416
        conn->wdata.active = false;
417
       
417
       
418
        /*
418
        /*
419
         * Note: the current fibril will be rescheduled either due to a
419
         * Note: the current fibril will be rescheduled either due to a
420
         * timeout or due to an arriving message destined to it. In the
420
         * timeout or due to an arriving message destined to it. In the
421
         * former case, handle_expired_timeouts() and, in the latter
421
         * former case, handle_expired_timeouts() and, in the latter
422
         * case, route_call() will perform the wakeup.
422
         * case, route_call() will perform the wakeup.
423
         */
423
         */
424
        fibril_switch(FIBRIL_TO_MANAGER);
424
        fibril_switch(FIBRIL_TO_MANAGER);
425
       
425
       
426
        /*
426
        /*
427
         * Futex is up after getting back from async_manager.
427
         * Futex is up after getting back from async_manager.
428
         * Get it again.
428
         * Get it again.
429
         */
429
         */
430
        futex_down(&async_futex);
430
        futex_down(&async_futex);
431
        if ((usecs) && (conn->wdata.timedout)
431
        if ((usecs) && (conn->wdata.timedout)
432
            && (list_empty(&conn->msg_queue))) {
432
            && (list_empty(&conn->msg_queue))) {
433
            /* If we timed out -> exit */
433
            /* If we timed out -> exit */
434
            futex_up(&async_futex);
434
            futex_up(&async_futex);
435
            return 0;
435
            return 0;
436
        }
436
        }
437
    }
437
    }
438
   
438
   
439
    msg_t *msg = list_get_instance(conn->msg_queue.next, msg_t, link);
439
    msg_t *msg = list_get_instance(conn->msg_queue.next, msg_t, link);
440
    list_remove(&msg->link);
440
    list_remove(&msg->link);
441
   
441
   
442
    ipc_callid_t callid = msg->callid;
442
    ipc_callid_t callid = msg->callid;
443
    *call = msg->call;
443
    *call = msg->call;
444
    free(msg);
444
    free(msg);
445
   
445
   
446
    futex_up(&async_futex);
446
    futex_up(&async_futex);
447
    return callid;
447
    return callid;
448
}
448
}
449
 
449
 
450
/** Default fibril function that gets called to handle new connection.
450
/** Default fibril function that gets called to handle new connection.
451
 *
451
 *
452
 * This function is defined as a weak symbol - to be redefined in user code.
452
 * This function is defined as a weak symbol - to be redefined in user code.
453
 *
453
 *
454
 * @param callid Hash of the incoming call.
454
 * @param callid Hash of the incoming call.
455
 * @param call   Data of the incoming call.
455
 * @param call   Data of the incoming call.
456
 *
456
 *
457
 */
457
 */
458
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)
458
static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)
459
{
459
{
460
    ipc_answer_0(callid, ENOENT);
460
    ipc_answer_0(callid, ENOENT);
461
}
461
}
462
 
462
 
463
/** Default fibril function that gets called to handle interrupt notifications.
463
/** Default fibril function that gets called to handle interrupt notifications.
464
 *
464
 *
465
 * This function is defined as a weak symbol - to be redefined in user code.
465
 * This function is defined as a weak symbol - to be redefined in user code.
466
 *
466
 *
467
 * @param callid Hash of the incoming call.
467
 * @param callid Hash of the incoming call.
468
 * @param call   Data of the incoming call.
468
 * @param call   Data of the incoming call.
469
 *
469
 *
470
 */
470
 */
471
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
471
static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
472
{
472
{
473
}
473
}
474
 
474
 
475
/** Wrapper for client connection fibril.
475
/** Wrapper for client connection fibril.
476
 *
476
 *
477
 * When a new connection arrives, a fibril with this implementing function is
477
 * When a new connection arrives, a fibril with this implementing function is
478
 * created. It calls client_connection() and does the final cleanup.
478
 * created. It calls client_connection() and does the final cleanup.
479
 *
479
 *
480
 * @param arg Connection structure pointer.
480
 * @param arg Connection structure pointer.
481
 *
481
 *
482
 * @return Always zero.
482
 * @return Always zero.
483
 *
483
 *
484
 */
484
 */
485
static int connection_fibril(void *arg)
485
static int connection_fibril(void *arg)
486
{
486
{
487
    /*
487
    /*
488
     * Setup fibril-local connection pointer and call client_connection().
488
     * Setup fibril-local connection pointer and call client_connection().
489
     *
489
     *
490
     */
490
     */
491
    FIBRIL_connection = (connection_t *) arg;
491
    FIBRIL_connection = (connection_t *) arg;
492
    FIBRIL_connection->cfibril(FIBRIL_connection->callid,
492
    FIBRIL_connection->cfibril(FIBRIL_connection->callid,
493
        &FIBRIL_connection->call);
493
        &FIBRIL_connection->call);
494
   
494
   
495
    /* Remove myself from the connection hash table */
495
    /* Remove myself from the connection hash table */
496
    futex_down(&async_futex);
496
    futex_down(&async_futex);
497
    unsigned long key = FIBRIL_connection->in_phone_hash;
497
    unsigned long key = FIBRIL_connection->in_phone_hash;
498
    hash_table_remove(&conn_hash_table, &key, 1);
498
    hash_table_remove(&conn_hash_table, &key, 1);
499
    futex_up(&async_futex);
499
    futex_up(&async_futex);
500
   
500
   
501
    /* Answer all remaining messages with EHANGUP */
501
    /* Answer all remaining messages with EHANGUP */
502
    while (!list_empty(&FIBRIL_connection->msg_queue)) {
502
    while (!list_empty(&FIBRIL_connection->msg_queue)) {
503
        msg_t *msg
503
        msg_t *msg
504
            = list_get_instance(FIBRIL_connection->msg_queue.next, msg_t, link);
504
            = list_get_instance(FIBRIL_connection->msg_queue.next, msg_t, link);
505
       
505
       
506
        list_remove(&msg->link);
506
        list_remove(&msg->link);
507
        ipc_answer_0(msg->callid, EHANGUP);
507
        ipc_answer_0(msg->callid, EHANGUP);
508
        free(msg);
508
        free(msg);
509
    }
509
    }
510
   
510
   
511
    if (FIBRIL_connection->close_callid)
511
    if (FIBRIL_connection->close_callid)
512
        ipc_answer_0(FIBRIL_connection->close_callid, EOK);
512
        ipc_answer_0(FIBRIL_connection->close_callid, EOK);
513
   
513
   
514
    return 0;
514
    return 0;
515
}
515
}
516
 
516
 
517
/** Create a new fibril for a new connection.
517
/** Create a new fibril for a new connection.
518
 *
518
 *
519
 * Create new fibril for connection, fill in connection structures and inserts
519
 * Create new fibril for connection, fill in connection structures and inserts
520
 * it into the hash table, so that later we can easily do routing of messages to
520
 * it into the hash table, so that later we can easily do routing of messages to
521
 * particular fibrils.
521
 * particular fibrils.
522
 *
522
 *
523
 * @param in_phone_hash Identification of the incoming connection.
523
 * @param in_phone_hash Identification of the incoming connection.
524
 * @param callid        Hash of the opening IPC_M_CONNECT_ME_TO call.
524
 * @param callid        Hash of the opening IPC_M_CONNECT_ME_TO call.
525
 *                      If callid is zero, the connection was opened by
525
 *                      If callid is zero, the connection was opened by
526
 *                      accepting the IPC_M_CONNECT_TO_ME call and this function
526
 *                      accepting the IPC_M_CONNECT_TO_ME call and this function
527
 *                      is called directly by the server.
527
 *                      is called directly by the server.
528
 * @param call          Call data of the opening call.
528
 * @param call          Call data of the opening call.
529
 * @param cfibril       Fibril function that should be called upon opening the
529
 * @param cfibril       Fibril function that should be called upon opening the
530
 *                      connection.
530
 *                      connection.
531
 *
531
 *
532
 * @return New fibril id or NULL on failure.
532
 * @return New fibril id or NULL on failure.
533
 *
533
 *
534
 */
534
 */
535
fid_t async_new_connection(ipcarg_t in_phone_hash, ipc_callid_t callid,
535
fid_t async_new_connection(ipcarg_t in_phone_hash, ipc_callid_t callid,
536
    ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *))
536
    ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *))
537
{
537
{
538
    connection_t *conn = malloc(sizeof(*conn));
538
    connection_t *conn = malloc(sizeof(*conn));
539
    if (!conn) {
539
    if (!conn) {
540
        if (callid)
540
        if (callid)
541
            ipc_answer_0(callid, ENOMEM);
541
            ipc_answer_0(callid, ENOMEM);
542
        return NULL;
542
        return NULL;
543
    }
543
    }
544
   
544
   
545
    conn->in_phone_hash = in_phone_hash;
545
    conn->in_phone_hash = in_phone_hash;
546
    list_initialize(&conn->msg_queue);
546
    list_initialize(&conn->msg_queue);
547
    conn->callid = callid;
547
    conn->callid = callid;
548
    conn->close_callid = false;
548
    conn->close_callid = false;
549
   
549
   
550
    if (call)
550
    if (call)
551
        conn->call = *call;
551
        conn->call = *call;
552
   
552
   
553
    /* We will activate the fibril ASAP */
553
    /* We will activate the fibril ASAP */
554
    conn->wdata.active = true;
554
    conn->wdata.active = true;
555
    conn->cfibril = cfibril;
555
    conn->cfibril = cfibril;
556
    conn->wdata.fid = fibril_create(connection_fibril, conn);
556
    conn->wdata.fid = fibril_create(connection_fibril, conn);
557
   
557
   
558
    if (!conn->wdata.fid) {
558
    if (!conn->wdata.fid) {
559
        free(conn);
559
        free(conn);
560
        if (callid)
560
        if (callid)
561
            ipc_answer_0(callid, ENOMEM);
561
            ipc_answer_0(callid, ENOMEM);
562
        return NULL;
562
        return NULL;
563
    }
563
    }
564
   
564
   
565
    /* Add connection to the connection hash table */
565
    /* Add connection to the connection hash table */
566
    ipcarg_t key = conn->in_phone_hash;
566
    ipcarg_t key = conn->in_phone_hash;
567
   
567
   
568
    futex_down(&async_futex);
568
    futex_down(&async_futex);
569
    hash_table_insert(&conn_hash_table, &key, &conn->link);
569
    hash_table_insert(&conn_hash_table, &key, &conn->link);
570
    futex_up(&async_futex);
570
    futex_up(&async_futex);
571
   
571
   
572
    fibril_add_ready(conn->wdata.fid);
572
    fibril_add_ready(conn->wdata.fid);
573
   
573
   
574
    return conn->wdata.fid;
574
    return conn->wdata.fid;
575
}
575
}
576
 
576
 
577
/** Handle a call that was received.
577
/** Handle a call that was received.
578
 *
578
 *
579
 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
579
 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
580
 * Otherwise the call is routed to its connection fibril.
580
 * Otherwise the call is routed to its connection fibril.
581
 *
581
 *
582
 * @param callid Hash of the incoming call.
582
 * @param callid Hash of the incoming call.
583
 * @param call   Data of the incoming call.
583
 * @param call   Data of the incoming call.
584
 *
584
 *
585
 */
585
 */
586
static void handle_call(ipc_callid_t callid, ipc_call_t *call)
586
static void handle_call(ipc_callid_t callid, ipc_call_t *call)
587
{
587
{
588
    /* Unrouted call - do some default behaviour */
588
    /* Unrouted call - do some default behaviour */
589
    if ((callid & IPC_CALLID_NOTIFICATION)) {
589
    if ((callid & IPC_CALLID_NOTIFICATION)) {
590
        process_notification(callid, call);
590
        process_notification(callid, call);
591
        return;
591
        return;
592
    }
592
    }
593
   
593
   
594
    switch (IPC_GET_METHOD(*call)) {
594
    switch (IPC_GET_METHOD(*call)) {
-
 
595
    case IPC_M_CONNECT_ME:
595
    case IPC_M_CONNECT_ME_TO:
596
    case IPC_M_CONNECT_ME_TO:
596
        /* Open new connection with fibril etc. */
597
        /* Open new connection with fibril etc. */
597
        async_new_connection(IPC_GET_ARG5(*call), callid, call,
598
        async_new_connection(IPC_GET_ARG5(*call), callid, call,
598
            client_connection);
599
            client_connection);
599
        return;
600
        return;
600
    }
601
    }
601
   
602
   
602
    /* Try to route the call through the connection hash table */
603
    /* Try to route the call through the connection hash table */
603
    if (route_call(callid, call))
604
    if (route_call(callid, call))
604
        return;
605
        return;
605
   
606
   
606
    /* Unknown call from unknown phone - hang it up */
607
    /* Unknown call from unknown phone - hang it up */
607
    ipc_answer_0(callid, EHANGUP);
608
    ipc_answer_0(callid, EHANGUP);
608
}
609
}
609
 
610
 
610
/** Fire all timeouts that expired. */
611
/** Fire all timeouts that expired. */
611
static void handle_expired_timeouts(void)
612
static void handle_expired_timeouts(void)
612
{
613
{
613
    struct timeval tv;
614
    struct timeval tv;
614
    gettimeofday(&tv, NULL);
615
    gettimeofday(&tv, NULL);
615
   
616
   
616
    futex_down(&async_futex);
617
    futex_down(&async_futex);
617
   
618
   
618
    link_t *cur = timeout_list.next;
619
    link_t *cur = timeout_list.next;
619
    while (cur != &timeout_list) {
620
    while (cur != &timeout_list) {
620
        awaiter_t *waiter = list_get_instance(cur, awaiter_t, link);
621
        awaiter_t *waiter = list_get_instance(cur, awaiter_t, link);
621
       
622
       
622
        if (tv_gt(&waiter->expires, &tv))
623
        if (tv_gt(&waiter->expires, &tv))
623
            break;
624
            break;
624
       
625
       
625
        cur = cur->next;
626
        cur = cur->next;
626
       
627
       
627
        list_remove(&waiter->link);
628
        list_remove(&waiter->link);
628
        waiter->inlist = false;
629
        waiter->inlist = false;
629
        waiter->timedout = true;
630
        waiter->timedout = true;
630
       
631
       
631
        /*
632
        /*
632
         * Redundant condition?
633
         * Redundant condition?
633
         * The fibril should not be active when it gets here.
634
         * The fibril should not be active when it gets here.
634
         */
635
         */
635
        if (!waiter->active) {
636
        if (!waiter->active) {
636
            waiter->active = true;
637
            waiter->active = true;
637
            fibril_add_ready(waiter->fid);
638
            fibril_add_ready(waiter->fid);
638
        }
639
        }
639
    }
640
    }
640
   
641
   
641
    futex_up(&async_futex);
642
    futex_up(&async_futex);
642
}
643
}
643
 
644
 
644
/** Endless loop dispatching incoming calls and answers.
645
/** Endless loop dispatching incoming calls and answers.
645
 *
646
 *
646
 * @return Never returns.
647
 * @return Never returns.
647
 *
648
 *
648
 */
649
 */
649
static int async_manager_worker(void)
650
static int async_manager_worker(void)
650
{
651
{
651
    while (true) {
652
    while (true) {
652
        if (fibril_switch(FIBRIL_FROM_MANAGER)) {
653
        if (fibril_switch(FIBRIL_FROM_MANAGER)) {
653
            futex_up(&async_futex);
654
            futex_up(&async_futex);
654
            /*
655
            /*
655
             * async_futex is always held when entering a manager
656
             * async_futex is always held when entering a manager
656
             * fibril.
657
             * fibril.
657
             */
658
             */
658
            continue;
659
            continue;
659
        }
660
        }
660
       
661
       
661
        futex_down(&async_futex);
662
        futex_down(&async_futex);
662
       
663
       
663
        suseconds_t timeout;
664
        suseconds_t timeout;
664
        if (!list_empty(&timeout_list)) {
665
        if (!list_empty(&timeout_list)) {
665
            awaiter_t *waiter
666
            awaiter_t *waiter
666
                = list_get_instance(timeout_list.next, awaiter_t, link);
667
                = list_get_instance(timeout_list.next, awaiter_t, link);
667
           
668
           
668
            struct timeval tv;
669
            struct timeval tv;
669
            gettimeofday(&tv, NULL);
670
            gettimeofday(&tv, NULL);
670
           
671
           
671
            if (tv_gteq(&tv, &waiter->expires)) {
672
            if (tv_gteq(&tv, &waiter->expires)) {
672
                futex_up(&async_futex);
673
                futex_up(&async_futex);
673
                handle_expired_timeouts();
674
                handle_expired_timeouts();
674
                continue;
675
                continue;
675
            } else
676
            } else
676
                timeout = tv_sub(&waiter->expires, &tv);
677
                timeout = tv_sub(&waiter->expires, &tv);
677
        } else
678
        } else
678
            timeout = SYNCH_NO_TIMEOUT;
679
            timeout = SYNCH_NO_TIMEOUT;
679
       
680
       
680
        futex_up(&async_futex);
681
        futex_up(&async_futex);
681
       
682
       
682
        ipc_call_t call;
683
        ipc_call_t call;
683
        ipc_callid_t callid
684
        ipc_callid_t callid
684
            = ipc_wait_cycle(&call, timeout, SYNCH_FLAGS_NONE);
685
            = ipc_wait_cycle(&call, timeout, SYNCH_FLAGS_NONE);
685
       
686
       
686
        if (!callid) {
687
        if (!callid) {
687
            handle_expired_timeouts();
688
            handle_expired_timeouts();
688
            continue;
689
            continue;
689
        }
690
        }
690
       
691
       
691
        if (callid & IPC_CALLID_ANSWERED)
692
        if (callid & IPC_CALLID_ANSWERED)
692
            continue;
693
            continue;
693
       
694
       
694
        handle_call(callid, &call);
695
        handle_call(callid, &call);
695
    }
696
    }
696
   
697
   
697
    return 0;
698
    return 0;
698
}
699
}
699
 
700
 
700
/** Function to start async_manager as a standalone fibril.
701
/** Function to start async_manager as a standalone fibril.
701
 *
702
 *
702
 * When more kernel threads are used, one async manager should exist per thread.
703
 * When more kernel threads are used, one async manager should exist per thread.
703
 *
704
 *
704
 * @param arg Unused.
705
 * @param arg Unused.
705
 * @return Never returns.
706
 * @return Never returns.
706
 *
707
 *
707
 */
708
 */
708
static int async_manager_fibril(void *arg)
709
static int async_manager_fibril(void *arg)
709
{
710
{
710
    futex_up(&async_futex);
711
    futex_up(&async_futex);
711
   
712
   
712
    /*
713
    /*
713
     * async_futex is always locked when entering manager
714
     * async_futex is always locked when entering manager
714
     */
715
     */
715
    async_manager_worker();
716
    async_manager_worker();
716
   
717
   
717
    return 0;
718
    return 0;
718
}
719
}
719
 
720
 
720
/** Add one manager to manager list. */
721
/** Add one manager to manager list. */
721
void async_create_manager(void)
722
void async_create_manager(void)
722
{
723
{
723
    fid_t fid = fibril_create(async_manager_fibril, NULL);
724
    fid_t fid = fibril_create(async_manager_fibril, NULL);
724
    fibril_add_manager(fid);
725
    fibril_add_manager(fid);
725
}
726
}
726
 
727
 
727
/** Remove one manager from manager list */
728
/** Remove one manager from manager list */
728
void async_destroy_manager(void)
729
void async_destroy_manager(void)
729
{
730
{
730
    fibril_remove_manager();
731
    fibril_remove_manager();
731
}
732
}
732
 
733
 
733
/** Initialize the async framework.
734
/** Initialize the async framework.
734
 *
735
 *
735
 * @return Zero on success or an error code.
736
 * @return Zero on success or an error code.
736
 */
737
 */
737
int _async_init(void)
738
int _async_init(void)
738
{
739
{
739
    if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
740
    if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
740
        &conn_hash_table_ops)) {
741
        &conn_hash_table_ops)) {
741
        printf("%s: cannot create hash table\n", "async");
742
        printf("%s: cannot create hash table\n", "async");
742
        return ENOMEM;
743
        return ENOMEM;
743
    }
744
    }
744
   
745
   
745
    return 0;
746
    return 0;
746
}
747
}
747
 
748
 
748
/** Reply received callback.
749
/** Reply received callback.
749
 *
750
 *
750
 * This function is called whenever a reply for an asynchronous message sent out
751
 * This function is called whenever a reply for an asynchronous message sent out
751
 * by the asynchronous framework is received.
752
 * by the asynchronous framework is received.
752
 *
753
 *
753
 * Notify the fibril which is waiting for this message that it has arrived.
754
 * Notify the fibril which is waiting for this message that it has arrived.
754
 *
755
 *
755
 * @param arg    Pointer to the asynchronous message record.
756
 * @param arg    Pointer to the asynchronous message record.
756
 * @param retval Value returned in the answer.
757
 * @param retval Value returned in the answer.
757
 * @param data   Call data of the answer.
758
 * @param data   Call data of the answer.
758
 */
759
 */
759
static void reply_received(void *arg, int retval, ipc_call_t *data)
760
static void reply_received(void *arg, int retval, ipc_call_t *data)
760
{
761
{
761
    amsg_t *msg = (amsg_t *) arg;
762
    amsg_t *msg = (amsg_t *) arg;
762
    msg->retval = retval;
763
    msg->retval = retval;
763
   
764
   
764
    futex_down(&async_futex);
765
    futex_down(&async_futex);
765
   
766
   
766
    /* Copy data after futex_down, just in case the call was detached */
767
    /* Copy data after futex_down, just in case the call was detached */
767
    if (msg->dataptr)
768
    if (msg->dataptr)
768
        *msg->dataptr = *data;
769
        *msg->dataptr = *data;
769
   
770
   
770
    write_barrier();
771
    write_barrier();
771
   
772
   
772
    /* Remove message from timeout list */
773
    /* Remove message from timeout list */
773
    if (msg->wdata.inlist)
774
    if (msg->wdata.inlist)
774
        list_remove(&msg->wdata.link);
775
        list_remove(&msg->wdata.link);
775
   
776
   
776
    msg->done = true;
777
    msg->done = true;
777
    if (!msg->wdata.active) {
778
    if (!msg->wdata.active) {
778
        msg->wdata.active = true;
779
        msg->wdata.active = true;
779
        fibril_add_ready(msg->wdata.fid);
780
        fibril_add_ready(msg->wdata.fid);
780
    }
781
    }
781
   
782
   
782
    futex_up(&async_futex);
783
    futex_up(&async_futex);
783
}
784
}
784
 
785
 
785
/** Send message and return id of the sent message.
786
/** Send message and return id of the sent message.
786
 *
787
 *
787
 * The return value can be used as input for async_wait() to wait for
788
 * The return value can be used as input for async_wait() to wait for
788
 * completion.
789
 * completion.
789
 *
790
 *
790
 * @param phoneid Handle of the phone that will be used for the send.
791
 * @param phoneid Handle of the phone that will be used for the send.
791
 * @param method  Service-defined method.
792
 * @param method  Service-defined method.
792
 * @param arg1    Service-defined payload argument.
793
 * @param arg1    Service-defined payload argument.
793
 * @param arg2    Service-defined payload argument.
794
 * @param arg2    Service-defined payload argument.
794
 * @param arg3    Service-defined payload argument.
795
 * @param arg3    Service-defined payload argument.
795
 * @param arg4    Service-defined payload argument.
796
 * @param arg4    Service-defined payload argument.
796
 * @param dataptr If non-NULL, storage where the reply data will be
797
 * @param dataptr If non-NULL, storage where the reply data will be
797
 *                stored.
798
 *                stored.
798
 *
799
 *
799
 * @return Hash of the sent message or 0 on error.
800
 * @return Hash of the sent message or 0 on error.
800
 *
801
 *
801
 */
802
 */
802
aid_t async_send_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,
803
aid_t async_send_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,
803
    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipc_call_t *dataptr)
804
    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipc_call_t *dataptr)
804
{
805
{
805
    amsg_t *msg = malloc(sizeof(*msg));
806
    amsg_t *msg = malloc(sizeof(*msg));
806
   
807
   
807
    if (!msg)
808
    if (!msg)
808
        return 0;
809
        return 0;
809
   
810
   
810
    msg->done = false;
811
    msg->done = false;
811
    msg->dataptr = dataptr;
812
    msg->dataptr = dataptr;
812
   
813
   
813
    msg->wdata.inlist = false;
814
    msg->wdata.inlist = false;
814
    /* We may sleep in the next method, but it will use its own mechanism */
815
    /* We may sleep in the next method, but it will use its own mechanism */
815
    msg->wdata.active = true;
816
    msg->wdata.active = true;
816
   
817
   
817
    ipc_call_async_4(phoneid, method, arg1, arg2, arg3, arg4, msg,
818
    ipc_call_async_4(phoneid, method, arg1, arg2, arg3, arg4, msg,
818
        reply_received, true);
819
        reply_received, true);
819
   
820
   
820
    return (aid_t) msg;
821
    return (aid_t) msg;
821
}
822
}
822
 
823
 
823
/** Send message and return id of the sent message
824
/** Send message and return id of the sent message
824
 *
825
 *
825
 * The return value can be used as input for async_wait() to wait for
826
 * The return value can be used as input for async_wait() to wait for
826
 * completion.
827
 * completion.
827
 *
828
 *
828
 * @param phoneid Handle of the phone that will be used for the send.
829
 * @param phoneid Handle of the phone that will be used for the send.
829
 * @param method  Service-defined method.
830
 * @param method  Service-defined method.
830
 * @param arg1    Service-defined payload argument.
831
 * @param arg1    Service-defined payload argument.
831
 * @param arg2    Service-defined payload argument.
832
 * @param arg2    Service-defined payload argument.
832
 * @param arg3    Service-defined payload argument.
833
 * @param arg3    Service-defined payload argument.
833
 * @param arg4    Service-defined payload argument.
834
 * @param arg4    Service-defined payload argument.
834
 * @param arg5    Service-defined payload argument.
835
 * @param arg5    Service-defined payload argument.
835
 * @param dataptr If non-NULL, storage where the reply data will be
836
 * @param dataptr If non-NULL, storage where the reply data will be
836
 *                stored.
837
 *                stored.
837
 *
838
 *
838
 * @return Hash of the sent message or 0 on error.
839
 * @return Hash of the sent message or 0 on error.
839
 *
840
 *
840
 */
841
 */
841
aid_t async_send_slow(int phoneid, ipcarg_t method, ipcarg_t arg1,
842
aid_t async_send_slow(int phoneid, ipcarg_t method, ipcarg_t arg1,
842
    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5,
843
    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5,
843
    ipc_call_t *dataptr)
844
    ipc_call_t *dataptr)
844
{
845
{
845
    amsg_t *msg = malloc(sizeof(*msg));
846
    amsg_t *msg = malloc(sizeof(*msg));
846
   
847
   
847
    if (!msg)
848
    if (!msg)
848
        return 0;
849
        return 0;
849
   
850
   
850
    msg->done = false;
851
    msg->done = false;
851
    msg->dataptr = dataptr;
852
    msg->dataptr = dataptr;
852
   
853
   
853
    msg->wdata.inlist = false;
854
    msg->wdata.inlist = false;
854
    /* We may sleep in next method, but it will use its own mechanism */
855
    /* We may sleep in next method, but it will use its own mechanism */
855
    msg->wdata.active = true;
856
    msg->wdata.active = true;
856
   
857
   
857
    ipc_call_async_5(phoneid, method, arg1, arg2, arg3, arg4, arg5, msg,
858
    ipc_call_async_5(phoneid, method, arg1, arg2, arg3, arg4, arg5, msg,
858
        reply_received, true);
859
        reply_received, true);
859
   
860
   
860
    return (aid_t) msg;
861
    return (aid_t) msg;
861
}
862
}
862
 
863
 
863
/** Wait for a message sent by the async framework.
864
/** Wait for a message sent by the async framework.
864
 *
865
 *
865
 * @param amsgid Hash of the message to wait for.
866
 * @param amsgid Hash of the message to wait for.
866
 * @param retval Pointer to storage where the retval of the answer will
867
 * @param retval Pointer to storage where the retval of the answer will
867
 *               be stored.
868
 *               be stored.
868
 *
869
 *
869
 */
870
 */
870
void async_wait_for(aid_t amsgid, ipcarg_t *retval)
871
void async_wait_for(aid_t amsgid, ipcarg_t *retval)
871
{
872
{
872
    amsg_t *msg = (amsg_t *) amsgid;
873
    amsg_t *msg = (amsg_t *) amsgid;
873
   
874
   
874
    futex_down(&async_futex);
875
    futex_down(&async_futex);
875
    if (msg->done) {
876
    if (msg->done) {
876
        futex_up(&async_futex);
877
        futex_up(&async_futex);
877
        goto done;
878
        goto done;
878
    }
879
    }
879
   
880
   
880
    msg->wdata.fid = fibril_get_id();
881
    msg->wdata.fid = fibril_get_id();
881
    msg->wdata.active = false;
882
    msg->wdata.active = false;
882
    msg->wdata.inlist = false;
883
    msg->wdata.inlist = false;
883
   
884
   
884
    /* Leave the async_futex locked when entering this function */
885
    /* Leave the async_futex locked when entering this function */
885
    fibril_switch(FIBRIL_TO_MANAGER);
886
    fibril_switch(FIBRIL_TO_MANAGER);
886
   
887
   
887
    /* Futex is up automatically after fibril_switch */
888
    /* Futex is up automatically after fibril_switch */
888
   
889
   
889
done:
890
done:
890
    if (retval)
891
    if (retval)
891
        *retval = msg->retval;
892
        *retval = msg->retval;
892
   
893
   
893
    free(msg);
894
    free(msg);
894
}
895
}
895
 
896
 
896
/** Wait for a message sent by the async framework, timeout variant.
897
/** Wait for a message sent by the async framework, timeout variant.
897
 *
898
 *
898
 * @param amsgid  Hash of the message to wait for.
899
 * @param amsgid  Hash of the message to wait for.
899
 * @param retval  Pointer to storage where the retval of the answer will
900
 * @param retval  Pointer to storage where the retval of the answer will
900
 *                be stored.
901
 *                be stored.
901
 * @param timeout Timeout in microseconds.
902
 * @param timeout Timeout in microseconds.
902
 *
903
 *
903
 * @return Zero on success, ETIMEOUT if the timeout has expired.
904
 * @return Zero on success, ETIMEOUT if the timeout has expired.
904
 *
905
 *
905
 */
906
 */
906
int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout)
907
int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout)
907
{
908
{
908
    amsg_t *msg = (amsg_t *) amsgid;
909
    amsg_t *msg = (amsg_t *) amsgid;
909
   
910
   
910
    /* TODO: Let it go through the event read at least once */
911
    /* TODO: Let it go through the event read at least once */
911
    if (timeout < 0)
912
    if (timeout < 0)
912
        return ETIMEOUT;
913
        return ETIMEOUT;
913
   
914
   
914
    futex_down(&async_futex);
915
    futex_down(&async_futex);
915
    if (msg->done) {
916
    if (msg->done) {
916
        futex_up(&async_futex);
917
        futex_up(&async_futex);
917
        goto done;
918
        goto done;
918
    }
919
    }
919
   
920
   
920
    gettimeofday(&msg->wdata.expires, NULL);
921
    gettimeofday(&msg->wdata.expires, NULL);
921
    tv_add(&msg->wdata.expires, timeout);
922
    tv_add(&msg->wdata.expires, timeout);
922
   
923
   
923
    msg->wdata.fid = fibril_get_id();
924
    msg->wdata.fid = fibril_get_id();
924
    msg->wdata.active = false;
925
    msg->wdata.active = false;
925
    insert_timeout(&msg->wdata);
926
    insert_timeout(&msg->wdata);
926
   
927
   
927
    /* Leave the async_futex locked when entering this function */
928
    /* Leave the async_futex locked when entering this function */
928
    fibril_switch(FIBRIL_TO_MANAGER);
929
    fibril_switch(FIBRIL_TO_MANAGER);
929
   
930
   
930
    /* Futex is up automatically after fibril_switch */
931
    /* Futex is up automatically after fibril_switch */
931
   
932
   
932
    if (!msg->done)
933
    if (!msg->done)
933
        return ETIMEOUT;
934
        return ETIMEOUT;
934
   
935
   
935
done:
936
done:
936
    if (retval)
937
    if (retval)
937
        *retval = msg->retval;
938
        *retval = msg->retval;
938
   
939
   
939
    free(msg);
940
    free(msg);
940
   
941
   
941
    return 0;
942
    return 0;
942
}
943
}
943
 
944
 
944
/** Wait for specified time.
945
/** Wait for specified time.
945
 *
946
 *
946
 * The current fibril is suspended but the thread continues to execute.
947
 * The current fibril is suspended but the thread continues to execute.
947
 *
948
 *
948
 * @param timeout Duration of the wait in microseconds.
949
 * @param timeout Duration of the wait in microseconds.
949
 *
950
 *
950
 */
951
 */
951
void async_usleep(suseconds_t timeout)
952
void async_usleep(suseconds_t timeout)
952
{
953
{
953
    amsg_t *msg = malloc(sizeof(*msg));
954
    amsg_t *msg = malloc(sizeof(*msg));
954
   
955
   
955
    if (!msg)
956
    if (!msg)
956
        return;
957
        return;
957
   
958
   
958
    msg->wdata.fid = fibril_get_id();
959
    msg->wdata.fid = fibril_get_id();
959
    msg->wdata.active = false;
960
    msg->wdata.active = false;
960
   
961
   
961
    gettimeofday(&msg->wdata.expires, NULL);
962
    gettimeofday(&msg->wdata.expires, NULL);
962
    tv_add(&msg->wdata.expires, timeout);
963
    tv_add(&msg->wdata.expires, timeout);
963
   
964
   
964
    futex_down(&async_futex);
965
    futex_down(&async_futex);
965
   
966
   
966
    insert_timeout(&msg->wdata);
967
    insert_timeout(&msg->wdata);
967
   
968
   
968
    /* Leave the async_futex locked when entering this function */
969
    /* Leave the async_futex locked when entering this function */
969
    fibril_switch(FIBRIL_TO_MANAGER);
970
    fibril_switch(FIBRIL_TO_MANAGER);
970
   
971
   
971
    /* Futex is up automatically after fibril_switch() */
972
    /* Futex is up automatically after fibril_switch() */
972
   
973
   
973
    free(msg);
974
    free(msg);
974
}
975
}
975
 
976
 
976
/** Setter for client_connection function pointer.
977
/** Setter for client_connection function pointer.
977
 *
978
 *
978
 * @param conn Function that will implement a new connection fibril.
979
 * @param conn Function that will implement a new connection fibril.
979
 *
980
 *
980
 */
981
 */
981
void async_set_client_connection(async_client_conn_t conn)
982
void async_set_client_connection(async_client_conn_t conn)
982
{
983
{
983
    client_connection = conn;
984
    client_connection = conn;
984
}
985
}
985
 
986
 
986
/** Setter for interrupt_received function pointer.
987
/** Setter for interrupt_received function pointer.
987
 *
988
 *
988
 * @param intr Function that will implement a new interrupt
989
 * @param intr Function that will implement a new interrupt
989
 *             notification fibril.
990
 *             notification fibril.
990
 */
991
 */
991
void async_set_interrupt_received(async_client_conn_t intr)
992
void async_set_interrupt_received(async_client_conn_t intr)
992
{
993
{
993
    interrupt_received = intr;
994
    interrupt_received = intr;
994
}
995
}
995
 
996
 
996
/** Pseudo-synchronous message sending - fast version.
997
/** Pseudo-synchronous message sending - fast version.
997
 *
998
 *
998
 * Send message asynchronously and return only after the reply arrives.
999
 * Send message asynchronously and return only after the reply arrives.
999
 *
1000
 *
1000
 * This function can only transfer 4 register payload arguments. For
1001
 * This function can only transfer 4 register payload arguments. For
1001
 * transferring more arguments, see the slower async_req_slow().
1002
 * transferring more arguments, see the slower async_req_slow().
1002
 *
1003
 *
1003
 * @param phoneid Hash of the phone through which to make the call.
1004
 * @param phoneid Hash of the phone through which to make the call.
1004
 * @param method  Method of the call.
1005
 * @param method  Method of the call.
1005
 * @param arg1    Service-defined payload argument.
1006
 * @param arg1    Service-defined payload argument.
1006
 * @param arg2    Service-defined payload argument.
1007
 * @param arg2    Service-defined payload argument.
1007
 * @param arg3    Service-defined payload argument.
1008
 * @param arg3    Service-defined payload argument.
1008
 * @param arg4    Service-defined payload argument.
1009
 * @param arg4    Service-defined payload argument.
1009
 * @param r1      If non-NULL, storage for the 1st reply argument.
1010
 * @param r1      If non-NULL, storage for the 1st reply argument.
1010
 * @param r2      If non-NULL, storage for the 2nd reply argument.
1011
 * @param r2      If non-NULL, storage for the 2nd reply argument.
1011
 * @param r3      If non-NULL, storage for the 3rd reply argument.
1012
 * @param r3      If non-NULL, storage for the 3rd reply argument.
1012
 * @param r4      If non-NULL, storage for the 4th reply argument.
1013
 * @param r4      If non-NULL, storage for the 4th reply argument.
1013
 * @param r5      If non-NULL, storage for the 5th reply argument.
1014
 * @param r5      If non-NULL, storage for the 5th reply argument.
1014
 *
1015
 *
1015
 * @return Return code of the reply or a negative error code.
1016
 * @return Return code of the reply or a negative error code.
1016
 *
1017
 *
1017
 */
1018
 */
1018
ipcarg_t async_req_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,
1019
ipcarg_t async_req_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,
1019
    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t *r1, ipcarg_t *r2,
1020
    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t *r1, ipcarg_t *r2,
1020
    ipcarg_t *r3, ipcarg_t *r4, ipcarg_t *r5)
1021
    ipcarg_t *r3, ipcarg_t *r4, ipcarg_t *r5)
1021
{
1022
{
1022
    ipc_call_t result;
1023
    ipc_call_t result;
1023
    aid_t eid = async_send_4(phoneid, method, arg1, arg2, arg3, arg4,
1024
    aid_t eid = async_send_4(phoneid, method, arg1, arg2, arg3, arg4,
1024
        &result);
1025
        &result);
1025
   
1026
   
1026
    ipcarg_t rc;
1027
    ipcarg_t rc;
1027
    async_wait_for(eid, &rc);
1028
    async_wait_for(eid, &rc);
1028
   
1029
   
1029
    if (r1)
1030
    if (r1)
1030
        *r1 = IPC_GET_ARG1(result);
1031
        *r1 = IPC_GET_ARG1(result);
1031
   
1032
   
1032
    if (r2)
1033
    if (r2)
1033
        *r2 = IPC_GET_ARG2(result);
1034
        *r2 = IPC_GET_ARG2(result);
1034
   
1035
   
1035
    if (r3)
1036
    if (r3)
1036
        *r3 = IPC_GET_ARG3(result);
1037
        *r3 = IPC_GET_ARG3(result);
1037
   
1038
   
1038
    if (r4)
1039
    if (r4)
1039
        *r4 = IPC_GET_ARG4(result);
1040
        *r4 = IPC_GET_ARG4(result);
1040
   
1041
   
1041
    if (r5)
1042
    if (r5)
1042
        *r5 = IPC_GET_ARG5(result);
1043
        *r5 = IPC_GET_ARG5(result);
1043
   
1044
   
1044
    return rc;
1045
    return rc;
1045
}
1046
}
1046
 
1047
 
1047
/** Pseudo-synchronous message sending - slow version.
1048
/** Pseudo-synchronous message sending - slow version.
1048
 *
1049
 *
1049
 * Send message asynchronously and return only after the reply arrives.
1050
 * Send message asynchronously and return only after the reply arrives.
1050
 *
1051
 *
1051
 * @param phoneid Hash of the phone through which to make the call.
1052
 * @param phoneid Hash of the phone through which to make the call.
1052
 * @param method  Method of the call.
1053
 * @param method  Method of the call.
1053
 * @param arg1    Service-defined payload argument.
1054
 * @param arg1    Service-defined payload argument.
1054
 * @param arg2    Service-defined payload argument.
1055
 * @param arg2    Service-defined payload argument.
1055
 * @param arg3    Service-defined payload argument.
1056
 * @param arg3    Service-defined payload argument.
1056
 * @param arg4    Service-defined payload argument.
1057
 * @param arg4    Service-defined payload argument.
1057
 * @param arg5    Service-defined payload argument.
1058
 * @param arg5    Service-defined payload argument.
1058
 * @param r1      If non-NULL, storage for the 1st reply argument.
1059
 * @param r1      If non-NULL, storage for the 1st reply argument.
1059
 * @param r2      If non-NULL, storage for the 2nd reply argument.
1060
 * @param r2      If non-NULL, storage for the 2nd reply argument.
1060
 * @param r3      If non-NULL, storage for the 3rd reply argument.
1061
 * @param r3      If non-NULL, storage for the 3rd reply argument.
1061
 * @param r4      If non-NULL, storage for the 4th reply argument.
1062
 * @param r4      If non-NULL, storage for the 4th reply argument.
1062
 * @param r5      If non-NULL, storage for the 5th reply argument.
1063
 * @param r5      If non-NULL, storage for the 5th reply argument.
1063
 *
1064
 *
1064
 * @return Return code of the reply or a negative error code.
1065
 * @return Return code of the reply or a negative error code.
1065
 *
1066
 *
1066
 */
1067
 */
1067
ipcarg_t async_req_slow(int phoneid, ipcarg_t method, ipcarg_t arg1,
1068
ipcarg_t async_req_slow(int phoneid, ipcarg_t method, ipcarg_t arg1,
1068
    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5, ipcarg_t *r1,
1069
    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5, ipcarg_t *r1,
1069
    ipcarg_t *r2, ipcarg_t *r3, ipcarg_t *r4, ipcarg_t *r5)
1070
    ipcarg_t *r2, ipcarg_t *r3, ipcarg_t *r4, ipcarg_t *r5)
1070
{
1071
{
1071
    ipc_call_t result;
1072
    ipc_call_t result;
1072
    aid_t eid = async_send_5(phoneid, method, arg1, arg2, arg3, arg4, arg5,
1073
    aid_t eid = async_send_5(phoneid, method, arg1, arg2, arg3, arg4, arg5,
1073
        &result);
1074
        &result);
1074
   
1075
   
1075
    ipcarg_t rc;
1076
    ipcarg_t rc;
1076
    async_wait_for(eid, &rc);
1077
    async_wait_for(eid, &rc);
1077
   
1078
   
1078
    if (r1)
1079
    if (r1)
1079
        *r1 = IPC_GET_ARG1(result);
1080
        *r1 = IPC_GET_ARG1(result);
1080
   
1081
   
1081
    if (r2)
1082
    if (r2)
1082
        *r2 = IPC_GET_ARG2(result);
1083
        *r2 = IPC_GET_ARG2(result);
1083
   
1084
   
1084
    if (r3)
1085
    if (r3)
1085
        *r3 = IPC_GET_ARG3(result);
1086
        *r3 = IPC_GET_ARG3(result);
1086
   
1087
   
1087
    if (r4)
1088
    if (r4)
1088
        *r4 = IPC_GET_ARG4(result);
1089
        *r4 = IPC_GET_ARG4(result);
1089
   
1090
   
1090
    if (r5)
1091
    if (r5)
1091
        *r5 = IPC_GET_ARG5(result);
1092
        *r5 = IPC_GET_ARG5(result);
1092
   
1093
   
1093
    return rc;
1094
    return rc;
1094
}
1095
}
1095
 
1096
 
1096
/** @}
1097
/** @}
1097
 */
1098
 */
1098
 
1099