int dill_msleep(int64_t deadline) { /* Return ECANCELED if shutting down. */ int rc = dill_canblock(); if(dill_slow(rc < 0)) return -1; /* Actual waiting. */ struct dill_tmclause tmcl; dill_timer(&tmcl, 1, deadline); int id = dill_wait(); if(dill_slow(id < 0)) return -1; return 0; }
int dill_msleep(int64_t deadline, const char *where) { /* Return ECANCELED if shutting down. */ int rc = dill_canblock(); if(dill_slow(rc < 0)) return -1; /* Trivial case. No waiting, but we do want a context switch. */ if(dill_slow(deadline == 0)) return yield(); /* Actual waiting. */ struct dill_tmcl tmcl; if(deadline > 0) dill_timer(&tmcl, 1, deadline); int id = dill_wait(where); if(dill_slow(id < 0)) return -1; dill_assert(id == 1); return 0; }
/* Gets called when coroutine handle is closed. */ static void dill_cr_close(struct dill_hvfs *vfs) { struct dill_ctx_cr *ctx = &dill_getctx->cr; struct dill_cr *cr = dill_cont(vfs, struct dill_cr, vfs); /* If the coroutine has already finished, we are done. */ if(!cr->done) { /* No blocking calls from this point on. */ cr->no_blocking1 = 1; /* Resume the coroutine if it was blocked. */ if(!cr->ready.next) dill_cancel(cr, ECANCELED); /* Wait for the coroutine to stop executing. With no clauses added, the only mechanism to resume is through dill_cancel(). This is not really a blocking call, although it looks like one. Given that the coroutine that is being shut down is not permitted to block, we should get control back pretty quickly. */ cr->closer = ctx->r; int rc = dill_wait(); /* This assertion triggers when coroutine tries to close a bundle that it is part of. There's no sane way to handle that so let's just crash the process. */ dill_assert(!(rc == -1 && errno == ECANCELED)); dill_assert(rc == -1 && errno == 0); } #if defined DILL_CENSUS /* Find the first overwritten byte on the stack. Determine stack usage based on that. */ uint8_t *bottom = ((uint8_t*)cr) - cr->stacksz; int i; for(i = 0; i != cr->stacksz; ++i) { if(bottom[i] != 0xa0 + (i % 13)) { /* dill_cr is located on the stack so we have to take that into account. Also, it may be necessary to align the top of the stack to a 16-byte boundary, so add 16 bytes to account for that. */ size_t used = cr->stacksz - i - sizeof(struct dill_cr) + 16; if(used > cr->census->max_stack) cr->census->max_stack = used; break; } } #endif #if defined DILL_VALGRIND VALGRIND_STACK_DEREGISTER(cr->sid); #endif /* Now that the coroutine is finished, deallocate it. */ if(!cr->mem) dill_freestack(cr + 1); }
int dill_fdout(int fd, int64_t deadline) { /* Return ECANCELED if shutting down. */ int rc = dill_canblock(); if(dill_slow(rc < 0)) return -1; /* Start waiting for the fd. */ struct dill_fdclause fdcl; rc = dill_pollset_out(&fdcl, 1, fd); if(dill_slow(rc < 0)) return -1; /* Optionally, start waiting for a timer. */ struct dill_tmclause tmcl; dill_timer(&tmcl, 2, deadline); /* Block. */ int id = dill_wait(); if(dill_slow(id < 0)) return -1; if(dill_slow(id == 2)) {errno = ETIMEDOUT; return -1;} return 0; }
int dill_bundle_wait(int h, int64_t deadline) { int rc = dill_canblock(); if(dill_slow(rc < 0)) return -1; struct dill_bundle *self = dill_hquery(h, dill_bundle_type); if(dill_slow(!self)) return -1; /* If there are no coroutines in the bundle succeed immediately. */ if(dill_list_empty(&self->crs)) return 0; /* Otherwise wait for all coroutines to finish. */ struct dill_clause cl; self->waiter = &cl; dill_waitfor(&cl, 0, NULL); struct dill_tmclause tmcl; dill_timer(&tmcl, 1, deadline); int id = dill_wait(); self->waiter = NULL; if(dill_slow(id < 0)) return -1; if(dill_slow(id == 1)) {errno = ETIMEDOUT; return -1;} dill_assert(id == 0); return 0; }
int dill_fdout(int fd, int64_t deadline, const char *where) { /* TODO: deadline == 0? */ /* Return ECANCELED if shutting down. */ int rc = dill_canblock(); if(dill_slow(rc < 0)) return -1; /* Start waiting for the fd. */ struct dill_clause fdcl; rc = dill_out(&fdcl, 1, fd); if(dill_slow(rc < 0)) return -1; /* Optionally, start waiting for a timer. */ struct dill_tmcl tmcl; if(deadline > 0) dill_timer(&tmcl, 2, deadline); /* Block. */ int id = dill_wait(where); if(dill_slow(id < 0)) return -1; if(dill_slow(id == 2)) {errno = ETIMEDOUT; return -1;} dill_assert(id == 1); return 0; }
/* The final part of go(). Gets called when the coroutine is finished. */ void dill_epilogue(void) { struct dill_ctx_cr *ctx = &dill_getctx->cr; /* Mark the coroutine as finished. */ ctx->r->done = 1; /* If there's a coroutine waiting for us to finish, unblock it now. */ if(ctx->r->closer) dill_cancel(ctx->r->closer, 0); /* Deallocate the coroutine, unless, of course, it is already in the process of being closed. */ if(!ctx->r->no_blocking1) { /* If this is the last coroutine in the bundle and there's someone waiting for hdone() on the bundle unblock them. */ if(dill_list_oneitem(&ctx->r->bundle)) { struct dill_bundle *b = dill_cont(ctx->r->bundle.next, struct dill_bundle, crs); if(b->waiter) dill_trigger(b->waiter, 0); } dill_list_erase(&ctx->r->bundle); dill_cr_close(&ctx->r->vfs); } /* With no clauses added, this call will never return. */ dill_assert(dill_slist_empty(&ctx->r->clauses)); dill_wait(); }