Subversion Repositories HelenOS

Rev

Rev 4055 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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