Subversion Repositories HelenOS

Rev

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

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