Skip to content

Commit 3508980

Browse files
committed
Simplify async/await paths
This removes quite a bit of complexity from how async/await works: * The connection_coro_yield enum doesn't have any CONN_CORO_ASYNC_AWAIT_* value anymore, because the CONN_CORO_WANT_* values are used instead. * epoll flags are now set using the same function that awaitv uses, and that resume_coro() used to use, so there's no need to pack the file descriptor and the interest in the coroutine yield value anymore. * resume_coro() is now simplified, not needing to do any work related to async/await anymore, as that's now performed by the async_await_fd() auxiliary function: so now you only pay the (cheap!) price if you're using this feature. In addition, signaling if an awaited file descriptor was hung up is done differently, via the return value of the async/await functions.
1 parent 802cf50 commit 3508980

File tree

3 files changed

+192
-153
lines changed

3 files changed

+192
-153
lines changed

src/lib/lwan-thread.c

+112-99
Original file line numberDiff line numberDiff line change
@@ -639,23 +639,23 @@ static void unasync_await_conn(void *data1, void *data2)
639639
}
640640

641641
static enum lwan_connection_coro_yield
642-
resume_async(const struct lwan *l,
643-
enum lwan_connection_coro_yield yield_result,
644-
int await_fd,
645-
struct lwan_connection *conn,
646-
int epoll_fd)
642+
prepare_await(const struct lwan *l,
643+
enum lwan_connection_coro_yield yield_result,
644+
int await_fd,
645+
struct lwan_connection *conn,
646+
int epoll_fd)
647647
{
648648
static const enum lwan_connection_flags to_connection_flags[] = {
649-
[CONN_CORO_ASYNC_AWAIT_READ] = CONN_EVENTS_READ,
650-
[CONN_CORO_ASYNC_AWAIT_WRITE] = CONN_EVENTS_WRITE,
651-
[CONN_CORO_ASYNC_AWAIT_READ_WRITE] = CONN_EVENTS_READ_WRITE,
649+
[CONN_CORO_WANT_READ] = CONN_EVENTS_READ,
650+
[CONN_CORO_WANT_WRITE] = CONN_EVENTS_WRITE,
651+
[CONN_CORO_WANT_READ_WRITE] = CONN_EVENTS_READ_WRITE,
652652
};
653653
enum lwan_connection_flags flags;
654654
int op;
655655

656656
assert(await_fd >= 0);
657-
assert(yield_result >= CONN_CORO_ASYNC_AWAIT_READ &&
658-
yield_result <= CONN_CORO_ASYNC_AWAIT_READ_WRITE);
657+
assert(yield_result >= CONN_CORO_WANT_READ &&
658+
yield_result <= CONN_CORO_WANT_READ_WRITE);
659659

660660
flags = to_connection_flags[yield_result];
661661

@@ -700,160 +700,181 @@ resume_async(const struct lwan *l,
700700
return CONN_CORO_ABORT;
701701
}
702702

703-
struct flag_update {
704-
unsigned int num_awaiting;
705-
enum lwan_connection_coro_yield request_conn_yield;
706-
};
707-
708-
static void reset_conn_async_awaitv_flag(struct lwan_connection *conns,
709-
va_list ap_orig)
703+
static void clear_awaitv_flags(struct lwan_connection *conns, va_list ap_orig)
710704
{
711705
va_list ap;
712706

713707
va_copy(ap, ap_orig);
714-
715-
while (true) {
716-
int await_fd = va_arg(ap, int);
717-
if (await_fd < 0) {
718-
va_end(ap);
719-
break;
720-
}
721-
722-
conns[await_fd].flags &= ~CONN_ASYNC_AWAITV;
723-
708+
for (int fd = va_arg(ap, int); fd >= 0; fd = va_arg(ap, int)) {
709+
conns[fd].flags &= ~CONN_ASYNC_AWAITV;
724710
LWAN_NO_DISCARD(va_arg(ap, enum lwan_connection_coro_yield));
725711
}
712+
va_end(ap);
726713
}
727714

728-
static struct flag_update
729-
update_flags_for_async_awaitv(struct lwan_request *r, struct lwan *l, va_list ap)
715+
struct awaitv_state {
716+
unsigned int num_awaiting;
717+
enum lwan_connection_coro_yield request_conn_yield;
718+
};
719+
720+
static int prepare_awaitv(struct lwan_request *r,
721+
struct lwan *l,
722+
va_list ap,
723+
struct awaitv_state *state)
730724
{
731725
int epoll_fd = r->conn->thread->epoll_fd;
732-
struct flag_update update = {.num_awaiting = 0,
733-
.request_conn_yield = CONN_CORO_YIELD};
734726

735-
reset_conn_async_awaitv_flag(l->conns, ap);
727+
*state = (struct awaitv_state){
728+
.num_awaiting = 0,
729+
.request_conn_yield = CONN_CORO_YIELD,
730+
};
736731

737-
while (true) {
738-
int await_fd = va_arg(ap, int);
739-
if (await_fd < 0) {
740-
return update;
741-
}
732+
clear_awaitv_flags(l->conns, ap);
742733

734+
for (int await_fd = va_arg(ap, int); await_fd >= 0;
735+
await_fd = va_arg(ap, int)) {
736+
struct lwan_connection *conn = &l->conns[await_fd];
743737
enum lwan_connection_coro_yield events =
744738
va_arg(ap, enum lwan_connection_coro_yield);
745-
if (UNLIKELY(events < CONN_CORO_ASYNC_AWAIT_READ ||
746-
events > CONN_CORO_ASYNC_AWAIT_READ_WRITE)) {
747-
lwan_status_error("awaitv() called with invalid events");
748-
coro_yield(r->conn->coro, CONN_CORO_ABORT);
749-
__builtin_unreachable();
750-
}
751-
752-
struct lwan_connection *conn = &l->conns[await_fd];
753739

740+
if (UNLIKELY(events < CONN_CORO_WANT_READ ||
741+
events > CONN_CORO_WANT_READ_WRITE)) {
742+
return -EINVAL;
743+
}
754744
if (UNLIKELY(conn->flags & CONN_ASYNC_AWAITV)) {
755745
lwan_status_debug("ignoring second awaitv call on same fd: %d",
756746
await_fd);
757747
continue;
758748
}
759749

760750
conn->flags |= CONN_ASYNC_AWAITV;
761-
update.num_awaiting++;
751+
state->num_awaiting++;
762752

763753
if (await_fd == r->fd) {
764-
static const enum lwan_connection_coro_yield to_request_yield[] = {
765-
[CONN_CORO_ASYNC_AWAIT_READ] = CONN_CORO_WANT_READ,
766-
[CONN_CORO_ASYNC_AWAIT_WRITE] = CONN_CORO_WANT_WRITE,
767-
[CONN_CORO_ASYNC_AWAIT_READ_WRITE] = CONN_CORO_WANT_READ_WRITE,
768-
};
769-
770-
update.request_conn_yield = to_request_yield[events];
754+
state->request_conn_yield = events;
771755
continue;
772756
}
773-
774-
events = resume_async(l, events, await_fd, r->conn, epoll_fd);
775-
if (UNLIKELY(events == CONN_CORO_ABORT)) {
757+
if (UNLIKELY(prepare_await(l, events, await_fd, r->conn, epoll_fd) ==
758+
CONN_CORO_ABORT)) {
776759
lwan_status_error("could not register fd for async operation");
777-
coro_yield(r->conn->coro, CONN_CORO_ABORT);
778-
__builtin_unreachable();
760+
return -EIO;
779761
}
780762
}
763+
764+
return 0;
781765
}
782766

783767
int lwan_request_awaitv_any(struct lwan_request *r, ...)
784768
{
785769
struct lwan *l = r->conn->thread->lwan;
770+
struct awaitv_state state;
786771
va_list ap;
787772

788773
va_start(ap, r);
789-
struct flag_update update = update_flags_for_async_awaitv(r, l, ap);
774+
int ret = prepare_awaitv(r, l, ap, &state);
790775
va_end(ap);
791776

777+
if (UNLIKELY(ret < 0)) {
778+
errno = -ret;
779+
lwan_status_critical_perror("prepare_awaitv()");
780+
coro_yield(r->conn->coro, CONN_CORO_ABORT);
781+
__builtin_unreachable();
782+
}
783+
792784
while (true) {
793-
int64_t v = coro_yield(r->conn->coro, update.request_conn_yield);
785+
int64_t v = coro_yield(r->conn->coro, state.request_conn_yield);
794786
struct lwan_connection *conn = (struct lwan_connection *)(uintptr_t)v;
795787

796-
if (conn->flags & CONN_ASYNC_AWAITV)
797-
return lwan_connection_get_fd(l, conn);
788+
if (conn->flags & CONN_ASYNC_AWAITV) {
789+
/* Ensure flags are unset in case awaitv_any() is called with
790+
* a different set of file descriptors. */
791+
va_start(ap, r);
792+
clear_awaitv_flags(l->conns, ap);
793+
va_end(ap);
794+
795+
int fd = lwan_connection_get_fd(l, conn);
796+
return UNLIKELY(conn->flags & CONN_HUNG_UP) ? -fd : fd;
797+
}
798798
}
799799
}
800800

801-
void lwan_request_awaitv_all(struct lwan_request *r, ...)
801+
int lwan_request_awaitv_all(struct lwan_request *r, ...)
802802
{
803803
struct lwan *l = r->conn->thread->lwan;
804+
struct awaitv_state state;
804805
va_list ap;
805806

806807
va_start(ap, r);
807-
struct flag_update update = update_flags_for_async_awaitv(r, l, ap);
808+
int ret = prepare_awaitv(r, l, ap, &state);
808809
va_end(ap);
809810

810-
while (update.num_awaiting) {
811-
int64_t v = coro_yield(r->conn->coro, update.request_conn_yield);
811+
if (UNLIKELY(ret < 0)) {
812+
errno = -ret;
813+
lwan_status_critical_perror("prepare_awaitv()");
814+
coro_yield(r->conn->coro, CONN_CORO_ABORT);
815+
__builtin_unreachable();
816+
}
817+
818+
for (ret = 0; state.num_awaiting;) {
819+
int64_t v = coro_yield(r->conn->coro, state.request_conn_yield);
812820
struct lwan_connection *conn = (struct lwan_connection *)(uintptr_t)v;
813821

814822
if (conn->flags & CONN_ASYNC_AWAITV) {
815823
conn->flags &= ~CONN_ASYNC_AWAITV;
816-
update.num_awaiting--;
824+
825+
if (UNLIKELY(conn->flags & CONN_HUNG_UP)) {
826+
/* Ensure flags are unset in case awaitv_any() is called with
827+
* a different set of file descriptors. */
828+
va_start(ap, r);
829+
clear_awaitv_flags(l->conns, ap);
830+
va_end(ap);
831+
832+
return lwan_connection_get_fd(l, conn);
833+
}
834+
835+
state.num_awaiting--;
817836
}
818837
}
819-
}
820-
821-
static inline int64_t
822-
make_async_yield_value(int fd, enum lwan_connection_coro_yield event)
823-
{
824-
assert(event >= CONN_CORO_ASYNC_AWAIT_READ &&
825-
event <= CONN_CORO_ASYNC_AWAIT_READ_WRITE);
826838

827-
return (int64_t)(((uint64_t)fd << 32 | event));
839+
return -1;
828840
}
829841

830842
static inline int async_await_fd(struct lwan_connection *conn,
831843
int fd,
832844
enum lwan_connection_coro_yield events)
833845
{
834-
int64_t yield_value = make_async_yield_value(fd, events);
835-
int64_t from_coro = coro_yield(conn->coro, yield_value);
836-
struct lwan_connection *conn_from_coro =
837-
(struct lwan_connection *)(intptr_t)from_coro;
846+
struct lwan_thread *thread = conn->thread;
847+
struct lwan *lwan = thread->lwan;
848+
enum lwan_connection_coro_yield yield =
849+
prepare_await(lwan, events, fd, conn, thread->epoll_fd);
838850

839-
assert(conn_from_coro->flags & CONN_ASYNC_AWAIT);
851+
if (LIKELY(yield == CONN_CORO_SUSPEND)) {
852+
int64_t v = coro_yield(conn->coro, yield);
840853

841-
return lwan_connection_get_fd(conn->thread->lwan, conn_from_coro);
854+
fd =
855+
lwan_connection_get_fd(lwan, (struct lwan_connection *)(intptr_t)v);
856+
857+
return UNLIKELY(conn->flags & CONN_HUNG_UP) ? -fd : fd;
858+
}
859+
860+
lwan_status_critical_perror("prepare_await(%d)", fd);
861+
coro_yield(conn->coro, CONN_CORO_ABORT);
862+
__builtin_unreachable();
842863
}
843864

844-
inline int lwan_request_await_read(struct lwan_request *r, int fd)
865+
int lwan_request_await_read(struct lwan_request *r, int fd)
845866
{
846-
return async_await_fd(r->conn, fd, CONN_CORO_ASYNC_AWAIT_READ);
867+
return async_await_fd(r->conn, fd, CONN_CORO_WANT_READ);
847868
}
848869

849-
inline int lwan_request_await_write(struct lwan_request *r, int fd)
870+
int lwan_request_await_write(struct lwan_request *r, int fd)
850871
{
851-
return async_await_fd(r->conn, fd, CONN_CORO_ASYNC_AWAIT_WRITE);
872+
return async_await_fd(r->conn, fd, CONN_CORO_WANT_WRITE);
852873
}
853874

854-
inline int lwan_request_await_read_write(struct lwan_request *r, int fd)
875+
int lwan_request_await_read_write(struct lwan_request *r, int fd)
855876
{
856-
return async_await_fd(r->conn, fd, CONN_CORO_ASYNC_AWAIT_READ_WRITE);
877+
return async_await_fd(r->conn, fd, CONN_CORO_WANT_READ_WRITE);
857878
}
858879

859880
static ALWAYS_INLINE void resume_coro(struct timeout_queue *tq,
@@ -864,20 +885,12 @@ static ALWAYS_INLINE void resume_coro(struct timeout_queue *tq,
864885
assert(conn_to_resume->coro);
865886
assert(conn_to_yield->coro);
866887

867-
int64_t from_coro = coro_resume_value(conn_to_resume->coro,
868-
(int64_t)(intptr_t)conn_to_yield);
869-
enum lwan_connection_coro_yield yield_result = from_coro & 0xffffffff;
870-
871-
if (UNLIKELY(yield_result >= CONN_CORO_ASYNC)) {
872-
int await_fd = (int)((uint64_t)from_coro >> 32);
873-
yield_result = resume_async(tq->lwan, yield_result, await_fd,
874-
conn_to_resume, epoll_fd);
875-
}
876-
877-
if (UNLIKELY(yield_result == CONN_CORO_ABORT)) {
888+
enum lwan_connection_coro_yield from_coro = coro_resume_value(
889+
conn_to_resume->coro, (int64_t)(intptr_t)conn_to_yield);
890+
if (UNLIKELY(from_coro == CONN_CORO_ABORT)) {
878891
timeout_queue_expire(tq, conn_to_resume);
879892
} else {
880-
update_epoll_flags(tq->lwan, conn_to_resume, epoll_fd, yield_result);
893+
update_epoll_flags(tq->lwan, conn_to_resume, epoll_fd, from_coro);
881894
timeout_queue_move_to_last(tq, conn_to_resume);
882895
}
883896
}

src/lib/lwan.h

+1-11
Original file line numberDiff line numberDiff line change
@@ -334,17 +334,7 @@ enum lwan_connection_coro_yield {
334334
CONN_CORO_SUSPEND,
335335
CONN_CORO_RESUME,
336336

337-
/* Group async stuff together to make it easier to check if a connection
338-
* coroutine is yielding because of async reasons. */
339-
CONN_CORO_ASYNC_AWAIT_READ,
340-
CONN_CORO_ASYNC_AWAIT_WRITE,
341-
CONN_CORO_ASYNC_AWAIT_READ_WRITE,
342-
343337
CONN_CORO_MAX,
344-
345-
/* Private API used by the async/await mechanism. Shouldn't be used
346-
* by handlers. */
347-
CONN_CORO_ASYNC = CONN_CORO_ASYNC_AWAIT_READ,
348338
};
349339

350340
struct lwan_key_value {
@@ -675,7 +665,7 @@ int lwan_request_await_read(struct lwan_request *r, int fd);
675665
int lwan_request_await_write(struct lwan_request *r, int fd);
676666
int lwan_request_await_read_write(struct lwan_request *r, int fd);
677667
int lwan_request_awaitv_any(struct lwan_request *r, ...);
678-
void lwan_request_awaitv_all(struct lwan_request *r, ...);
668+
int lwan_request_awaitv_all(struct lwan_request *r, ...);
679669
ssize_t lwan_request_async_read(struct lwan_request *r, int fd, void *buf, size_t len);
680670
ssize_t lwan_request_async_read_flags(struct lwan_request *request, int fd, void *buf, size_t len, int flags);
681671
ssize_t lwan_request_async_write(struct lwan_request *r, int fd, const void *buf, size_t len);

0 commit comments

Comments
 (0)