GCC Code Coverage Report


Directory: src/
File: src/event_loop.c
Date: 2026-06-18 18:44:18
Exec Total Coverage
Lines: 111 131 84.7%
Functions: 14 14 100.0%
Branches: 39 76 51.3%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2023 Egor Tensin <egor@tensin.name>
3 * This file is part of the "cimple" project.
4 * For details, see https://github.com/egor-tensin/cimple.
5 * Distributed under the MIT License.
6 */
7
8 #include "event_loop.h"
9
10 #include "log.h"
11 #include "string.h"
12
13 #include <poll.h>
14 #include <stddef.h>
15 #include <stdlib.h>
16 #include <sys/queue.h>
17
18 SIMPLEQ_HEAD(event_fd_queue, event_fd);
19
20 struct event_fd {
21 int fd;
22 short events;
23 event_handler handler;
24 void* arg;
25 int once;
26
27 SIMPLEQ_ENTRY(event_fd) entries;
28 };
29
30 36966 static struct event_fd* event_fd_create(int fd, short events, event_handler handler, void* arg) {
31 36966 struct event_fd* res = calloc(1, sizeof(struct event_fd));
32
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36966 times.
36966 if (!res) {
33 log_errno("calloc");
34 return NULL;
35 }
36
37 36966 res->fd = fd;
38 36966 res->events = events;
39 36966 res->handler = handler;
40 36966 res->arg = arg;
41 36966 res->once = 0;
42
43 36966 return res;
44 }
45
46 36966 static void event_fd_destroy(struct event_fd* entry) {
47 36966 free(entry);
48 36966 }
49
50 83 static void event_fd_queue_create(struct event_fd_queue* queue) {
51 83 SIMPLEQ_INIT(queue);
52 83 }
53
54 83 static void event_fd_queue_destroy(struct event_fd_queue* queue) {
55 83 struct event_fd* entry1 = SIMPLEQ_FIRST(queue);
56
2/2
✓ Branch 0 taken 166 times.
✓ Branch 1 taken 83 times.
249 while (entry1) {
57 166 struct event_fd* entry2 = SIMPLEQ_NEXT(entry1, entries);
58 166 event_fd_destroy(entry1);
59 166 entry1 = entry2;
60 }
61 83 SIMPLEQ_INIT(queue);
62 83 }
63
64 struct event_loop {
65 nfds_t nfds;
66 struct event_fd_queue entries;
67 };
68
69 83 int event_loop_create(struct event_loop** _loop) {
70 83 int ret = 0;
71
72 83 struct event_loop* loop = calloc(1, sizeof(struct event_loop));
73
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 if (!loop) {
74 log_errno("calloc");
75 return -1;
76 }
77 83 *_loop = loop;
78
79 83 event_fd_queue_create(&loop->entries);
80
81 83 return ret;
82 }
83
84 83 void event_loop_destroy(struct event_loop* loop) {
85 83 event_fd_queue_destroy(&loop->entries);
86 83 free(loop);
87 83 }
88
89 36966 static void event_loop_add_internal(struct event_loop* loop, struct event_fd* entry) {
90
1/2
✓ Branch 1 taken 36966 times.
✗ Branch 2 not taken.
36966 log_debug("Adding descriptor %d to event loop\n", entry->fd);
91
92 36966 nfds_t nfds = loop->nfds + 1;
93 36966 SIMPLEQ_INSERT_TAIL(&loop->entries, entry, entries);
94 36966 loop->nfds = nfds;
95 36966 }
96
97 112 int event_loop_add(
98 struct event_loop* loop,
99 int fd,
100 short events,
101 event_handler handler,
102 void* arg
103 ) {
104 112 struct event_fd* entry = event_fd_create(fd, events, handler, arg);
105
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (!entry)
106 return -1;
107 112 event_loop_add_internal(loop, entry);
108 112 return 0;
109 }
110
111 36854 int event_loop_add_once(
112 struct event_loop* loop,
113 int fd,
114 short events,
115 event_handler handler,
116 void* arg
117 ) {
118 36854 struct event_fd* entry = event_fd_create(fd, events, handler, arg);
119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36854 times.
36854 if (!entry)
120 return -1;
121 36854 entry->once = 1;
122 36854 event_loop_add_internal(loop, entry);
123 36854 return 0;
124 }
125
126 36800 static void event_loop_remove(struct event_loop* loop, struct event_fd* entry) {
127
1/2
✓ Branch 1 taken 36800 times.
✗ Branch 2 not taken.
36800 log_debug("Removing descriptor %d from event loop\n", entry->fd);
128
129
5/8
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 49368 times.
✓ Branch 5 taken 36800 times.
✓ Branch 6 taken 25540 times.
✓ Branch 7 taken 11260 times.
86168 SIMPLEQ_REMOVE(&loop->entries, entry, event_fd, entries);
130 36800 event_fd_destroy(entry);
131 36800 --loop->nfds;
132 36800 }
133
134 301075 static char* append_event(char* buf, size_t sz, char* ptr, const char* event) {
135
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 301075 times.
301075 if (ptr > buf)
136 ptr = string_append(ptr, buf + sz, ",");
137 301075 return string_append(ptr, buf + sz, event);
138 }
139
140 301075 static char* events_to_string(short events) {
141 301075 const size_t sz = 128;
142 301075 char* buf = calloc(1, sz);
143
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 301075 times.
301075 if (!buf)
144 return NULL;
145
146 301075 char* ptr = buf;
147
148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 301075 times.
301075 if (events & POLLNVAL)
149 ptr = append_event(buf, sz, ptr, "POLLNVAL");
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 301075 times.
301075 if (events & POLLERR)
151 ptr = append_event(buf, sz, ptr, "POLLERR");
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 301075 times.
301075 if (events & POLLHUP)
153 ptr = append_event(buf, sz, ptr, "POLLHUP");
154
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 301075 times.
301075 if (events & POLLRDHUP)
155 ptr = append_event(buf, sz, ptr, "POLLRDHUP");
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 301075 times.
301075 if (events & POLLPRI)
157 ptr = append_event(buf, sz, ptr, "POLLPRI");
158
1/2
✓ Branch 0 taken 301075 times.
✗ Branch 1 not taken.
301075 if (events & POLLIN)
159 301075 ptr = append_event(buf, sz, ptr, "POLLIN");
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 301075 times.
301075 if (events & POLLOUT)
161 ptr = append_event(buf, sz, ptr, "POLLOUT");
162
163 301075 return buf;
164 }
165
166 62356 static struct pollfd* make_pollfds(const struct event_loop* loop) {
167 62356 struct pollfd* fds = calloc(loop->nfds, sizeof(struct pollfd));
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62356 times.
62356 if (!fds) {
169 log_errno("calloc");
170 return NULL;
171 }
172
173 62356 struct event_fd* entry = SIMPLEQ_FIRST(&loop->entries);
174
2/2
✓ Branch 0 taken 236572 times.
✓ Branch 1 taken 62356 times.
298928 for (nfds_t i = 0; i < loop->nfds; ++i, entry = SIMPLEQ_NEXT(entry, entries)) {
175 236572 fds[i].fd = entry->fd;
176 236572 fds[i].events = entry->events;
177 }
178
179
1/2
✓ Branch 1 taken 62356 times.
✗ Branch 2 not taken.
62356 log_debug("Descriptors:\n");
180
2/2
✓ Branch 0 taken 236572 times.
✓ Branch 1 taken 62356 times.
298928 for (nfds_t i = 0; i < loop->nfds; ++i) {
181 236572 char* events = events_to_string(fds[i].events);
182
1/4
✓ Branch 1 taken 236572 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
236572 log_debug(" %d (%s)\n", fds[i].fd, events ? events : "");
183 236572 free(events);
184 }
185
186 62356 return fds;
187 }
188
189 62356 int event_loop_run(struct event_loop* loop) {
190 /* Cache the number of event descriptors so that event handlers can
191 * append new ones. */
192 62356 const nfds_t nfds = loop->nfds;
193
194 62356 struct pollfd* fds = make_pollfds(loop);
195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62356 times.
62356 if (!fds)
196 return -1;
197
198 62356 int ret = poll(fds, nfds, -1);
199
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62356 times.
62356 if (ret < 0) {
200 log_errno("poll");
201 return ret;
202 }
203 62356 ret = 0;
204
205 62356 struct event_fd* entry = SIMPLEQ_FIRST(&loop->entries);
206
2/2
✓ Branch 0 taken 236572 times.
✓ Branch 1 taken 62356 times.
298928 for (nfds_t i = 0; i < nfds; ++i) {
207 236572 struct event_fd* next = SIMPLEQ_NEXT(entry, entries);
208
209
2/2
✓ Branch 0 taken 172069 times.
✓ Branch 1 taken 64503 times.
236572 if (!fds[i].revents)
210 172069 goto next;
211
212 64503 char* events = events_to_string(fds[i].revents);
213
1/4
✓ Branch 1 taken 64503 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
64503 log_debug("Descriptor %d is ready: %s\n", fds[i].fd, events ? events : "");
214 64503 free(events);
215
216 /* Execute all handlers but notice if any of them fail. */
217 64503 const int handler_ret = entry->handler(loop, fds[i].fd, fds[i].revents, entry->arg);
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64503 times.
64503 if (handler_ret < 0)
219 ret = handler_ret;
220
221
2/2
✓ Branch 0 taken 27703 times.
✓ Branch 1 taken 36800 times.
64503 if (entry->once)
222 36800 event_loop_remove(loop, entry);
223
224 27703 next:
225 236572 entry = next;
226 236572 continue;
227 }
228
229 62356 free(fds);
230 62356 return ret;
231 }
232