Changeset 514d561 in mainline for uspace/lib/c/generic/async/client.c


Ignore:
Timestamp:
2018-07-20T16:27:20Z (7 years ago)
Author:
Jiří Zárevúcky <jiri.zarevucky@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
05208d9
Parents:
7137f74c
git-author:
Jiří Zárevúcky <jiri.zarevucky@…> (2018-07-19 21:52:47)
git-committer:
Jiří Zárevúcky <jiri.zarevucky@…> (2018-07-20 16:27:20)
Message:

Fibril/async implementation overhaul.

This commit marks the move towards treating the fibril library as a mere
implementation of a generic threading interface. Understood as a layer that
wraps the kernel threads, we not only have to wrap threading itself, but also
every syscall that blocks the kernel thread (by blocking, we mean thread not
doing useful work until an external event happens — e.g. locking a kernel
mutex or thread sleep is understood as blocking, but an as_area_create() is not,
despite potentially taking a long time to complete).

Consequently, we implement fibril_ipc_wait() as a fibril-native wrapper for
kernel's ipc_wait(), and also implement timer functionality like timeouts
as part of the fibril library. This removes the interdependency between fibril
implementation and the async framework — in theory, the fibril API could be
reimplemented as a simple 1:1 shim, and the async framework would continue
working normally (note that the current implementation of loader complicates
this).

To better isolate the fibril internals from the implementation of high-level
synchronization, a fibril_event_t is added. This object conceptually acts
like a single slot wait queue. All other synchronization is implemented in
terms of this primitive.

File:
1 edited

Legend:

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

    r7137f74c r514d561  
    104104#include <ipc/irq.h>
    105105#include <ipc/event.h>
    106 #include <futex.h>
    107106#include <fibril.h>
    108107#include <adt/hash_table.h>
     
    128127/** Message data */
    129128typedef struct {
    130         awaiter_t wdata;
     129        fibril_event_t received;
    131130
    132131        /** If reply was received. */
     
    136135        bool forget;
    137136
    138         /** If already destroyed. */
    139         bool destroyed;
    140 
    141137        /** Pointer to where the answer data is stored. */
    142138        ipc_call_t *dataptr;
     
    145141} amsg_t;
    146142
    147 static void to_event_initialize(to_event_t *to)
    148 {
    149         struct timeval tv = { 0, 0 };
    150 
    151         to->inlist = false;
    152         to->occurred = false;
    153         link_initialize(&to->link);
    154         to->expires = tv;
    155 }
    156 
    157 static void wu_event_initialize(wu_event_t *wu)
    158 {
    159         wu->inlist = false;
    160         link_initialize(&wu->link);
    161 }
    162 
    163 void awaiter_initialize(awaiter_t *aw)
    164 {
    165         aw->fid = 0;
    166         aw->active = false;
    167         to_event_initialize(&aw->to_event);
    168         wu_event_initialize(&aw->wu_event);
    169 }
    170 
    171143static amsg_t *amsg_create(void)
    172144{
    173         amsg_t *msg = malloc(sizeof(amsg_t));
    174         if (msg) {
    175                 msg->done = false;
    176                 msg->forget = false;
    177                 msg->destroyed = false;
    178                 msg->dataptr = NULL;
    179                 msg->retval = EINVAL;
    180                 awaiter_initialize(&msg->wdata);
    181         }
    182 
    183         return msg;
     145        return calloc(1, sizeof(amsg_t));
    184146}
    185147
    186148static void amsg_destroy(amsg_t *msg)
    187149{
    188         if (!msg)
    189                 return;
    190 
    191         assert(!msg->destroyed);
    192         msg->destroyed = true;
    193150        free(msg);
    194151}
     
    251208        msg->retval = IPC_GET_RETVAL(*data);
    252209
    253         /* Copy data after futex_down, just in case the call was detached */
     210        /* Copy data inside lock, just in case the call was detached */
    254211        if ((msg->dataptr) && (data))
    255212                *msg->dataptr = *data;
    256213
    257         write_barrier();
    258 
    259         /* Remove message from timeout list */
    260         if (msg->wdata.to_event.inlist)
    261                 list_remove(&msg->wdata.to_event.link);
    262 
    263214        msg->done = true;
    264215
    265216        if (msg->forget) {
    266                 assert(msg->wdata.active);
    267217                amsg_destroy(msg);
    268         } else if (!msg->wdata.active) {
    269                 msg->wdata.active = true;
    270                 fibril_add_ready(msg->wdata.fid);
     218        } else {
     219                fibril_notify(&msg->received);
    271220        }
    272221
     
    301250
    302251        msg->dataptr = dataptr;
    303         msg->wdata.active = true;
    304252
    305253        errno_t rc = ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3,
     
    343291
    344292        msg->dataptr = dataptr;
    345         msg->wdata.active = true;
    346293
    347294        errno_t rc = ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3,
     
    371318
    372319        amsg_t *msg = (amsg_t *) amsgid;
    373 
    374         futex_lock(&async_futex);
    375 
    376         assert(!msg->forget);
    377         assert(!msg->destroyed);
    378 
    379         if (msg->done) {
    380                 futex_unlock(&async_futex);
    381                 goto done;
    382         }
    383 
    384         msg->wdata.fid = fibril_get_id();
    385         msg->wdata.active = false;
    386         msg->wdata.to_event.inlist = false;
    387 
    388         /* Leave the async_futex locked when entering this function */
    389         fibril_switch(FIBRIL_FROM_BLOCKED);
    390         futex_unlock(&async_futex);
    391 
    392 done:
     320        fibril_wait_for(&msg->received);
     321
    393322        if (retval)
    394323                *retval = msg->retval;
     
    420349
    421350        amsg_t *msg = (amsg_t *) amsgid;
    422 
    423         futex_lock(&async_futex);
    424 
    425         assert(!msg->forget);
    426         assert(!msg->destroyed);
    427 
    428         if (msg->done) {
    429                 futex_unlock(&async_futex);
    430                 goto done;
    431         }
    432351
    433352        /*
     
    438357                timeout = 0;
    439358
    440         getuptime(&msg->wdata.to_event.expires);
    441         tv_add_diff(&msg->wdata.to_event.expires, timeout);
    442 
    443         /*
    444          * Current fibril is inserted as waiting regardless of the
    445          * "size" of the timeout.
    446          *
    447          * Checking for msg->done and immediately bailing out when
    448          * timeout == 0 would mean that the manager fibril would never
    449          * run (consider single threaded program).
    450          * Thus the IPC answer would be never retrieved from the kernel.
    451          *
    452          * Notice that the actual delay would be very small because we
    453          * - switch to manager fibril
    454          * - the manager sees expired timeout
    455          * - and thus adds us back to ready queue
    456          * - manager switches back to some ready fibril
    457          *   (prior it, it checks for incoming IPC).
    458          *
    459          */
    460         msg->wdata.fid = fibril_get_id();
    461         msg->wdata.active = false;
    462         async_insert_timeout(&msg->wdata);
    463 
    464         /* Leave the async_futex locked when entering this function */
    465         fibril_switch(FIBRIL_FROM_BLOCKED);
    466         futex_unlock(&async_futex);
    467 
    468         if (!msg->done)
    469                 return ETIMEOUT;
    470 
    471 done:
     359        struct timeval expires;
     360        getuptime(&expires);
     361        tv_add_diff(&expires, timeout);
     362
     363        errno_t rc = fibril_wait_timeout(&msg->received, &expires);
     364        if (rc != EOK)
     365                return rc;
     366
    472367        if (retval)
    473368                *retval = msg->retval;
     
    475370        amsg_destroy(msg);
    476371
    477         return 0;
     372        return EOK;
    478373}
    479374
     
    494389
    495390        assert(!msg->forget);
    496         assert(!msg->destroyed);
    497391
    498392        futex_lock(&async_futex);
     
    506400
    507401        futex_unlock(&async_futex);
    508 }
    509 
    510 /** Wait for specified time.
    511  *
    512  * The current fibril is suspended but the thread continues to execute.
    513  *
    514  * @param timeout Duration of the wait in microseconds.
    515  *
    516  */
    517 void fibril_usleep(suseconds_t timeout)
    518 {
    519         awaiter_t awaiter;
    520         awaiter_initialize(&awaiter);
    521 
    522         awaiter.fid = fibril_get_id();
    523 
    524         getuptime(&awaiter.to_event.expires);
    525         tv_add_diff(&awaiter.to_event.expires, timeout);
    526 
    527         futex_lock(&async_futex);
    528 
    529         async_insert_timeout(&awaiter);
    530 
    531         /* Leave the async_futex locked when entering this function */
    532         fibril_switch(FIBRIL_FROM_BLOCKED);
    533         futex_unlock(&async_futex);
    534 }
    535 
    536 /** Delay execution for the specified number of seconds
    537  *
    538  * @param sec Number of seconds to sleep
    539  */
    540 void fibril_sleep(unsigned int sec)
    541 {
    542         /*
    543          * Sleep in 1000 second steps to support
    544          * full argument range
    545          */
    546 
    547         while (sec > 0) {
    548                 unsigned int period = (sec > 1000) ? 1000 : sec;
    549 
    550                 fibril_usleep(period * 1000000);
    551                 sec -= period;
    552         }
    553402}
    554403
     
    716565
    717566        msg->dataptr = &result;
    718         msg->wdata.active = true;
    719567
    720568        errno_t rc = ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO,
Note: See TracChangeset for help on using the changeset viewer.