diff options
| author | Egor Tensin <Egor.Tensin@gmail.com> | 2023-06-12 01:42:08 +0200 |
|---|---|---|
| committer | Egor Tensin <Egor.Tensin@gmail.com> | 2023-06-13 01:37:08 +0200 |
| commit | 48ce9170b057ddd2165b0239a92aede15849f7a3 (patch) | |
| tree | c9928af6202081d9521107f1dc0ae362f54a6adc /src/command.c | |
| parent | log: refactoring (diff) | |
| download | cimple-48ce9170b057ddd2165b0239a92aede15849f7a3.tar.gz cimple-48ce9170b057ddd2165b0239a92aede15849f7a3.zip | |
use signalfd to stop on SIGTERM
Is this an overkill? I don't know.
The thing is, correctly intercepting SIGTERM (also SIGINT, etc.) is
incredibly tricky. For example, before this commit, my I/O loops in
server.c and worker.c were inherently racy.
This was immediately obvious if you tried to run the tests. The tests
(especially the Valgrind flavour) would run a worker, wait until it
prints a "Waiting for a new command" line, and try to kill it using
SIGTERM. The problem is, the global_stop_flag check could have already
been executed by the worker, and it would hang forever in recv().
The solution seems to be to use signalfd and select()/poll(). I've never
used either before, but it seems to work well enough - at least the very
same tests pass and don't hang now.
Diffstat (limited to 'src/command.c')
| -rw-r--r-- | src/command.c | 79 |
1 files changed, 72 insertions, 7 deletions
diff --git a/src/command.c b/src/command.c index bd58f05..6306f7e 100644 --- a/src/command.c +++ b/src/command.c @@ -6,6 +6,8 @@ */ #include "command.h" +#include "compiler.h" +#include "event_loop.h" #include "log.h" #include "msg.h" @@ -128,20 +130,29 @@ int cmd_dispatcher_handle(const struct cmd_dispatcher *dispatcher, const struct return cmd_dispatcher_handle_internal(dispatcher, command, result, dispatcher->ctx); } +static struct cmd_conn_ctx *make_conn_ctx(int fd, void *arg) +{ + struct cmd_conn_ctx *ctx = malloc(sizeof(struct cmd_conn_ctx)); + if (!ctx) { + log_errno("malloc"); + return NULL; + } + + ctx->fd = fd; + ctx->arg = arg; + + return ctx; +} + int cmd_dispatcher_handle_conn(int conn_fd, void *_dispatcher) { struct cmd_dispatcher *dispatcher = (struct cmd_dispatcher *)_dispatcher; struct msg *request = NULL, *response = NULL; int ret = 0; - struct cmd_conn_ctx *new_ctx = malloc(sizeof(struct cmd_conn_ctx)); - if (!new_ctx) { - log_errno("malloc"); + struct cmd_conn_ctx *new_ctx = make_conn_ctx(conn_fd, dispatcher->ctx); + if (!new_ctx) return -1; - } - - new_ctx->fd = conn_fd; - new_ctx->arg = dispatcher->ctx; ret = msg_recv(conn_fd, &request); if (ret < 0) @@ -168,3 +179,57 @@ free_ctx: return ret; } + +static int cmd_dispatcher_handle_event(UNUSED struct event_loop *loop, int fd, short revents, + void *_dispatcher) +{ + struct cmd_dispatcher *dispatcher = (struct cmd_dispatcher *)_dispatcher; + struct msg *request = NULL, *response = NULL; + int ret = 0; + + if (!(revents & POLLIN)) { + log_err("Descriptor %d is not readable\n", fd); + return -1; + } + + struct cmd_conn_ctx *new_ctx = make_conn_ctx(fd, dispatcher->ctx); + if (!new_ctx) + return -1; + + ret = msg_recv(fd, &request); + if (ret < 0) + goto free_ctx; + + ret = cmd_dispatcher_handle_internal(dispatcher, request, &response, new_ctx); + if (ret < 0) + goto free_response; + + if (response) { + ret = msg_send(fd, response); + if (ret < 0) + goto free_response; + } + +free_response: + if (response) + msg_free(response); + + msg_free(request); + +free_ctx: + free(new_ctx); + + return ret; +} + +int cmd_dispatcher_add_to_event_loop(const struct cmd_dispatcher *dispatcher, + struct event_loop *loop, int fd) +{ + struct event_fd entry = { + .fd = fd, + .events = POLLIN, + .handler = cmd_dispatcher_handle_event, + .arg = (void *)dispatcher, + }; + return event_loop_add(loop, &entry); +} |
