Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/generic/async.c

    r927a181e r8820544  
    101101#undef LIBC_ASYNC_C_
    102102
     103#include <ipc/irq.h>
     104#include <ipc/event.h>
    103105#include <futex.h>
    104106#include <fibril.h>
     
    109111#include <sys/time.h>
    110112#include <libarch/barrier.h>
    111 #include <bool.h>
     113#include <stdbool.h>
    112114#include <malloc.h>
    113115#include <mem.h>
     
    116118#include "private/libc.h"
    117119
    118 
    119120/** Session data */
    120121struct async_sess {
     
    166167
    167168/** Async framework global futex */
    168 futex_t async_futex = FUTEX_INITIALIZER;
     169atomic_t async_futex = FUTEX_INITIALIZER;
    169170
    170171/** Number of threads waiting for IPC in the kernel. */
     
    242243        async_client_conn_t cfibril;
    243244} connection_t;
     245
     246/* Notification data */
     247typedef struct {
     248        ht_link_t link;
     249       
     250        /** Notification method */
     251        sysarg_t imethod;
     252       
     253        /** Notification handler */
     254        async_notification_handler_t handler;
     255       
     256        /** Notification data */
     257        void *data;
     258} notification_t;
    244259
    245260/** Identifier of the incoming connection handled by the current fibril. */
     
    335350}
    336351
    337 /** Default fibril function that gets called to handle interrupt notifications.
    338  *
    339  * This function is defined as a weak symbol - to be redefined in user code.
    340  *
    341  * @param callid Hash of the incoming call.
    342  * @param call   Data of the incoming call.
    343  * @param arg    Local argument.
    344  *
    345  */
    346 static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
    347 {
    348 }
    349 
    350352static async_client_conn_t client_connection = default_client_connection;
    351 static async_interrupt_handler_t interrupt_received = default_interrupt_received;
     353static size_t notification_handler_stksz = FIBRIL_DFLT_STK_SIZE;
    352354
    353355/** Setter for client_connection function pointer.
     
    362364}
    363365
    364 /** Setter for interrupt_received function pointer.
    365  *
    366  * @param intr Function that will implement a new interrupt
    367  *             notification fibril.
    368  */
    369 void async_set_interrupt_received(async_interrupt_handler_t intr)
    370 {
    371         interrupt_received = intr;
     366/** Set the stack size for the notification handler notification fibrils.
     367 *
     368 * @param size Stack size in bytes.
     369 */
     370void async_set_notification_handler_stack_size(size_t size)
     371{
     372        notification_handler_stksz = size;
    372373}
    373374
     
    389390static hash_table_t client_hash_table;
    390391static hash_table_t conn_hash_table;
     392static hash_table_t notification_hash_table;
    391393static LIST_INITIALIZE(timeout_list);
    392394
    393 static size_t client_key_hash(void *k)
    394 {
    395         task_id_t key = *(task_id_t*)k;
    396         return key;
     395static sysarg_t notification_avail = 0;
     396
     397static size_t client_key_hash(void *key)
     398{
     399        task_id_t in_task_id = *(task_id_t *) key;
     400        return in_task_id;
    397401}
    398402
     
    403407}
    404408
    405 static bool client_key_equal(void *k, const ht_link_t *item)
    406 {
    407         task_id_t key = *(task_id_t*)k;
     409static bool client_key_equal(void *key, const ht_link_t *item)
     410{
     411        task_id_t in_task_id = *(task_id_t *) key;
    408412        client_t *client = hash_table_get_inst(item, client_t, link);
    409         return key == client->in_task_id;
    410 }
    411 
     413        return in_task_id == client->in_task_id;
     414}
    412415
    413416/** Operations for the client hash table. */
     
    429432static size_t conn_key_hash(void *key)
    430433{
    431         sysarg_t in_phone_hash  = *(sysarg_t*)key;
    432         return in_phone_hash ;
     434        sysarg_t in_phone_hash = *(sysarg_t *) key;
     435        return in_phone_hash;
    433436}
    434437
     
    441444static bool conn_key_equal(void *key, const ht_link_t *item)
    442445{
    443         sysarg_t in_phone_hash = *(sysarg_t*)key;
     446        sysarg_t in_phone_hash = *(sysarg_t *) key;
    444447        connection_t *conn = hash_table_get_inst(item, connection_t, link);
    445448        return (in_phone_hash == conn->in_phone_hash);
    446449}
    447 
    448450
    449451/** Operations for the connection hash table. */
     
    452454        .key_hash = conn_key_hash,
    453455        .key_equal = conn_key_equal,
     456        .equal = NULL,
     457        .remove_callback = NULL
     458};
     459
     460static size_t notification_key_hash(void *key)
     461{
     462        sysarg_t id = *(sysarg_t *) key;
     463        return id;
     464}
     465
     466static size_t notification_hash(const ht_link_t *item)
     467{
     468        notification_t *notification =
     469            hash_table_get_inst(item, notification_t, link);
     470        return notification_key_hash(&notification->imethod);
     471}
     472
     473static bool notification_key_equal(void *key, const ht_link_t *item)
     474{
     475        sysarg_t id = *(sysarg_t *) key;
     476        notification_t *notification =
     477            hash_table_get_inst(item, notification_t, link);
     478        return id == notification->imethod;
     479}
     480
     481/** Operations for the notification hash table. */
     482static hash_table_ops_t notification_hash_table_ops = {
     483        .hash = notification_hash,
     484        .key_hash = notification_key_hash,
     485        .key_equal = notification_key_equal,
    454486        .equal = NULL,
    455487        .remove_callback = NULL
     
    501533        futex_down(&async_futex);
    502534       
    503         ht_link_t *hlp = hash_table_find(&conn_hash_table, &call->in_phone_hash);
    504        
    505         if (!hlp) {
     535        ht_link_t *link = hash_table_find(&conn_hash_table, &call->in_phone_hash);
     536        if (!link) {
    506537                futex_up(&async_futex);
    507538                return false;
    508539        }
    509540       
    510         connection_t *conn = hash_table_get_inst(hlp, connection_t, link);
     541        connection_t *conn = hash_table_get_inst(link, connection_t, link);
    511542       
    512543        msg_t *msg = malloc(sizeof(*msg));
     
    543574 *
    544575 * When a notification arrives, a fibril with this implementing function is
    545  * created. It calls interrupt_received() and does the final cleanup.
     576 * created. It calls the corresponding notification handler and does the final
     577 * cleanup.
    546578 *
    547579 * @param arg Message structure pointer.
     
    555587       
    556588        msg_t *msg = (msg_t *) arg;
    557         interrupt_received(msg->callid, &msg->call);
     589        async_notification_handler_t handler = NULL;
     590        void *data = NULL;
     591       
     592        futex_down(&async_futex);
     593       
     594        ht_link_t *link = hash_table_find(&notification_hash_table,
     595            &IPC_GET_IMETHOD(msg->call));
     596        if (link) {
     597                notification_t *notification =
     598                    hash_table_get_inst(link, notification_t, link);
     599                handler = notification->handler;
     600                data = notification->data;
     601        }
     602       
     603        futex_up(&async_futex);
     604       
     605        if (handler)
     606                handler(msg->callid, &msg->call, data);
    558607       
    559608        free(msg);
     
    561610}
    562611
    563 /** Process interrupt notification.
     612/** Process notification.
    564613 *
    565614 * A new fibril is created which would process the notification.
     
    587636        msg->call = *call;
    588637       
    589         fid_t fid = fibril_create(notification_fibril, msg);
     638        fid_t fid = fibril_create_generic(notification_fibril, msg,
     639            notification_handler_stksz);
    590640        if (fid == 0) {
    591641                free(msg);
     
    598648        futex_up(&async_futex);
    599649        return true;
     650}
     651
     652/** Subscribe to IRQ notification.
     653 *
     654 * @param inr     IRQ number.
     655 * @param devno   Device number of the device generating inr.
     656 * @param handler Notification handler.
     657 * @param data    Notification handler client data.
     658 * @param ucode   Top-half pseudocode handler.
     659 *
     660 * @return Zero on success or a negative error code.
     661 *
     662 */
     663int async_irq_subscribe(int inr, int devno,
     664    async_notification_handler_t handler, void *data, const irq_code_t *ucode)
     665{
     666        notification_t *notification =
     667            (notification_t *) malloc(sizeof(notification_t));
     668        if (!notification)
     669                return ENOMEM;
     670       
     671        futex_down(&async_futex);
     672       
     673        sysarg_t imethod = notification_avail;
     674        notification_avail++;
     675       
     676        notification->imethod = imethod;
     677        notification->handler = handler;
     678        notification->data = data;
     679       
     680        hash_table_insert(&notification_hash_table, &notification->link);
     681       
     682        futex_up(&async_futex);
     683       
     684        return ipc_irq_subscribe(inr, devno, imethod, ucode);
     685}
     686
     687/** Unsubscribe from IRQ notification.
     688 *
     689 * @param inr     IRQ number.
     690 * @param devno   Device number of the device generating inr.
     691 *
     692 * @return Zero on success or a negative error code.
     693 *
     694 */
     695int async_irq_unsubscribe(int inr, int devno)
     696{
     697        // TODO: Remove entry from hash table
     698        //       to avoid memory leak
     699       
     700        return ipc_irq_unsubscribe(inr, devno);
     701}
     702
     703/** Subscribe to event notifications.
     704 *
     705 * @param evno    Event type to subscribe.
     706 * @param handler Notification handler.
     707 * @param data    Notification handler client data.
     708 *
     709 * @return Zero on success or a negative error code.
     710 *
     711 */
     712int async_event_subscribe(event_type_t evno,
     713    async_notification_handler_t handler, void *data)
     714{
     715        notification_t *notification =
     716            (notification_t *) malloc(sizeof(notification_t));
     717        if (!notification)
     718                return ENOMEM;
     719       
     720        futex_down(&async_futex);
     721       
     722        sysarg_t imethod = notification_avail;
     723        notification_avail++;
     724       
     725        notification->imethod = imethod;
     726        notification->handler = handler;
     727        notification->data = data;
     728       
     729        hash_table_insert(&notification_hash_table, &notification->link);
     730       
     731        futex_up(&async_futex);
     732       
     733        return ipc_event_subscribe(evno, imethod);
     734}
     735
     736/** Subscribe to task event notifications.
     737 *
     738 * @param evno    Event type to subscribe.
     739 * @param handler Notification handler.
     740 * @param data    Notification handler client data.
     741 *
     742 * @return Zero on success or a negative error code.
     743 *
     744 */
     745int async_event_task_subscribe(event_task_type_t evno,
     746    async_notification_handler_t handler, void *data)
     747{
     748        notification_t *notification =
     749            (notification_t *) malloc(sizeof(notification_t));
     750        if (!notification)
     751                return ENOMEM;
     752       
     753        futex_down(&async_futex);
     754       
     755        sysarg_t imethod = notification_avail;
     756        notification_avail++;
     757       
     758        notification->imethod = imethod;
     759        notification->handler = handler;
     760        notification->data = data;
     761       
     762        hash_table_insert(&notification_hash_table, &notification->link);
     763       
     764        futex_up(&async_futex);
     765       
     766        return ipc_event_task_subscribe(evno, imethod);
     767}
     768
     769/** Unmask event notifications.
     770 *
     771 * @param evno Event type to unmask.
     772 *
     773 * @return Value returned by the kernel.
     774 *
     775 */
     776int async_event_unmask(event_type_t evno)
     777{
     778        return ipc_event_unmask(evno);
     779}
     780
     781/** Unmask task event notifications.
     782 *
     783 * @param evno Event type to unmask.
     784 *
     785 * @return Value returned by the kernel.
     786 *
     787 */
     788int async_event_task_unmask(event_task_type_t evno)
     789{
     790        return ipc_event_task_unmask(evno);
    600791}
    601792
     
    691882
    692883        futex_down(&async_futex);
    693         ht_link_t *lnk = hash_table_find(&client_hash_table, &client_id);
    694         if (lnk) {
    695                 client = hash_table_get_inst(lnk, client_t, link);
     884        ht_link_t *link = hash_table_find(&client_hash_table, &client_id);
     885        if (link) {
     886                client = hash_table_get_inst(link, client_t, link);
    696887                atomic_inc(&client->refcnt);
    697888        } else if (create) {
     
    10951286       
    10961287        if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
     1288                abort();
     1289       
     1290        if (!hash_table_create(&notification_hash_table, 0, 0,
     1291            &notification_hash_table_ops))
    10971292                abort();
    10981293       
     
    20572252       
    20582253        async_sess_t *sess = exch->sess;
     2254        assert(sess != NULL);
    20592255       
    20602256        atomic_dec(&sess->refcnt);
     
    20782274 * @param arg   User defined argument.
    20792275 * @param flags Storage for the received flags. Can be NULL.
    2080  * @param dst   Destination address space area base. Cannot be NULL.
     2276 * @param dst   Address of the storage for the destination address space area
     2277 *              base address. Cannot be NULL.
    20812278 *
    20822279 * @return Zero on success or a negative error code from errno.h.
     
    22062403 *
    22072404 * @param callid Hash of the IPC_M_DATA_WRITE call to answer.
    2208  * @param dst    Destination address space area base address.
     2405 * @param dst    Address of the storage for the destination address space area
     2406 *               base address.
    22092407 *
    22102408 * @return Zero on success or a value from @ref errno.h on failure.
     
    22672465bool async_data_read_receive(ipc_callid_t *callid, size_t *size)
    22682466{
     2467        ipc_call_t data;
     2468        return async_data_read_receive_call(callid, &data, size);
     2469}
     2470
     2471/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
     2472 *
     2473 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
     2474 * calls so that the user doesn't have to remember the meaning of each IPC
     2475 * argument.
     2476 *
     2477 * So far, this wrapper is to be used from within a connection fibril.
     2478 *
     2479 * @param callid Storage for the hash of the IPC_M_DATA_READ.
     2480 * @param size   Storage for the maximum size. Can be NULL.
     2481 *
     2482 * @return True on success, false on failure.
     2483 *
     2484 */
     2485bool async_data_read_receive_call(ipc_callid_t *callid, ipc_call_t *data,
     2486    size_t *size)
     2487{
    22692488        assert(callid);
    2270        
    2271         ipc_call_t data;
    2272         *callid = async_get_call(&data);
    2273        
    2274         if (IPC_GET_IMETHOD(data) != IPC_M_DATA_READ)
     2489        assert(data);
     2490       
     2491        *callid = async_get_call(data);
     2492       
     2493        if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_READ)
    22752494                return false;
    22762495       
    22772496        if (size)
    2278                 *size = (size_t) IPC_GET_ARG2(data);
     2497                *size = (size_t) IPC_GET_ARG2(*data);
    22792498       
    22802499        return true;
     
    23712590bool async_data_write_receive(ipc_callid_t *callid, size_t *size)
    23722591{
     2592        ipc_call_t data;
     2593        return async_data_write_receive_call(callid, &data, size);
     2594}
     2595
     2596/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
     2597 *
     2598 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
     2599 * calls so that the user doesn't have to remember the meaning of each IPC
     2600 * argument.
     2601 *
     2602 * So far, this wrapper is to be used from within a connection fibril.
     2603 *
     2604 * @param callid Storage for the hash of the IPC_M_DATA_WRITE.
     2605 * @param data   Storage for the ipc call data.
     2606 * @param size   Storage for the suggested size. May be NULL.
     2607 *
     2608 * @return True on success, false on failure.
     2609 *
     2610 */
     2611bool async_data_write_receive_call(ipc_callid_t *callid, ipc_call_t *data,
     2612    size_t *size)
     2613{
    23732614        assert(callid);
    2374        
    2375         ipc_call_t data;
    2376         *callid = async_get_call(&data);
    2377        
    2378         if (IPC_GET_IMETHOD(data) != IPC_M_DATA_WRITE)
     2615        assert(data);
     2616       
     2617        *callid = async_get_call(data);
     2618       
     2619        if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_WRITE)
    23792620                return false;
    23802621       
    23812622        if (size)
    2382                 *size = (size_t) IPC_GET_ARG2(data);
     2623                *size = (size_t) IPC_GET_ARG2(*data);
    23832624       
    23842625        return true;
Note: See TracChangeset for help on using the changeset viewer.