aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/command.c
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2023-06-12 01:42:08 +0200
committerEgor Tensin <Egor.Tensin@gmail.com>2023-06-13 01:37:08 +0200
commit48ce9170b057ddd2165b0239a92aede15849f7a3 (patch)
treec9928af6202081d9521107f1dc0ae362f54a6adc /src/command.c
parentlog: refactoring (diff)
downloadcimple-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.c79
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);
+}