Subversion Repositories HelenOS

Rev

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

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