diff options
-rw-r--r-- | src/pulp.c | 69 | ||||
-rw-r--r-- | src/pulp.h | 11 |
2 files changed, 73 insertions, 7 deletions
@@ -57,6 +57,7 @@ typedef struct pulp_fiber_t { union { struct { pulp_usec_t alarm; + pulp_mailbox_t *mailbox; /* non-NULL if fiber receives mail */ } sleep; } state; @@ -409,10 +410,15 @@ pulp_fiber_t * pulp_self(pulp_t *pulp) /* sleep for the supplied number of microseconds (not public) */ -static void pulp_usleep(pulp_t *pulp, unsigned useconds) +/* if mailbox is non-NULL it may be used to receive mail while sleeping via pulp_fiber_get_mailslot() */ +static void pulp_usleep(pulp_t *pulp, unsigned useconds, pulp_mailbox_t *mailbox) { assert(pulp); + pulp->current->state.sleep.mailbox = mailbox; + if (mailbox) + mailbox->count = 0; + pulp->current->state.sleep.alarm = pulp->now + useconds; put_current_fiber(pulp, &pulp->fibers.sleep); pulp_schedule(pulp); @@ -420,20 +426,22 @@ static void pulp_usleep(pulp_t *pulp, unsigned useconds) /* sleep for the supplied number of milliseconds */ -void pulp_msleep(pulp_t *pulp, unsigned milliseconds) +/* if mailbox is non-NULL it may be used to receive mail while sleeping via pulp_fiber_get_mailslot() */ +void pulp_msleep(pulp_t *pulp, unsigned milliseconds, pulp_mailbox_t *mailbox) { assert(pulp); - return pulp_usleep(pulp, milliseconds * PULP_USECS_PER_MSEC); + return pulp_usleep(pulp, milliseconds * PULP_USECS_PER_MSEC, mailbox); } /* sleep for the supplied number of seconds */ -void pulp_sleep(pulp_t *pulp, unsigned seconds) +/* if mailbox is non-NULL it may be used to receive mail while sleeping via pulp_fiber_get_mailslot() */ +void pulp_sleep(pulp_t *pulp, unsigned seconds, pulp_mailbox_t *mailbox) { assert(pulp); - return pulp_usleep(pulp, seconds * PULP_USECS_PER_SEC); + return pulp_usleep(pulp, seconds * PULP_USECS_PER_SEC, mailbox); } @@ -447,6 +455,57 @@ pulp_usec_t pulp_now(pulp_t *pulp) } +/* Get a mailslot in a destination fiber's mailbox. + * If the fiber has no mailbox, -ENOENT is returned. + * If the mailbox is full, -ENOSPC is returned. + * On success 0 is returned and the mailslot pointer is stored @ *res_mailslot. + * + * The mailslot can only be treated as valid until the calling fiber sleeps, + * after sleeping the receiving fiber may execute, then free, or clear and + * reuse its mailbox. + * + * This interface returns a pointer to the slot rather than accepting something + * like a void * value to store at the slot to allow a greater variety of data + * passing models. If your fibers are creating mailboxes having slots filled + * with pointers to valid memory, then the sender may dereference the + * mailslot's pointer to the space for storing the message rather than having + * to allocate space for passing messages larger than a pointer. + * + * Of course, if your scenario is simple enough, you can always simply write to + * the mailslot's void * worth of space. There's also the possibility of not + * writing anything at all; if the only thing needed is a basic signal - the + * mailbox count will be advanced wether you do anything with the mailslot or + * not. It's up to you to define the contract for your fibers to agree on. + */ +int pulp_fiber_get_mailslot(pulp_t *pulp, pulp_fiber_t *fiber, void ***res_mailslot) +{ + assert(pulp); + assert(fiber); + assert(res_mailslot); + + /* XXX: this doesn't currently implement any blocking - if the mailbox + * is full or the fiber isn't receiving mail (no mailbox), we simply return + * failure. It seems trivial to make this fiber go to sleep when the + * mailbox is full then retrying when it comes back though. We'll see if + * that's desirable (reliable delivery). What I'm expecting is that mailboxes + * should just be sized to never experience blocking. + */ + + /* TODO: nothing is sanity checked at this time, I assume the supplied fiber + * is valid and sleeping. + */ + if (!fiber->state.sleep.mailbox) + return -ENOENT; + + if (fiber->state.sleep.mailbox->count >= fiber->state.sleep.mailbox->size) + return -ENOSPC; + + *res_mailslot = &fiber->state.sleep.mailbox->slots[fiber->state.sleep.mailbox->count++]; + + return 0; +} + + /* TODO: interfaces for idling/waking fibers */ /* TODO: interfaces for synchronization across fibers */ /* all these are left to be implemented as their needs arise */ @@ -24,14 +24,21 @@ typedef struct pulp_t pulp_t; typedef uint64_t pulp_usec_t; typedef struct thunk_t thunk_t; +/* for conveniences like trivial stack allocation, this is public */ +typedef struct pulp_mailbox_t { + unsigned size, count; + void *slots[]; +} pulp_mailbox_t; + pulp_t * pulp_new(void); void pulp_free(pulp_t *pulp); int pulp_tick(pulp_t *pulp, unsigned *next_tick_delay_us); void pulp_run(pulp_t *pulp); pulp_fiber_t * pulp_fiber_new(pulp_t *pulp, unsigned delay_ms, thunk_t *thunk); -void pulp_msleep(pulp_t *pulp, unsigned milliseconds); -void pulp_sleep(pulp_t *pulp, unsigned seconds); +void pulp_msleep(pulp_t *pulp, unsigned milliseconds, pulp_mailbox_t *mailbox); +void pulp_sleep(pulp_t *pulp, unsigned seconds, pulp_mailbox_t *mailbox); pulp_usec_t pulp_now(pulp_t *pulp); pulp_fiber_t * pulp_self(pulp_t *pulp); +int pulp_fiber_get_mailslot(pulp_t *pulp, pulp_fiber_t *fiber, void ***res_mailslot); #endif |