Subversion Repositories HelenOS

Rev

Rev 4327 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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