summaryrefslogtreecommitdiffstats
path: root/common/loop.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/loop.c')
-rw-r--r--common/loop.c180
1 files changed, 180 insertions, 0 deletions
diff --git a/common/loop.c b/common/loop.c
new file mode 100644
index 00000000..750bee75
--- /dev/null
+++ b/common/loop.c
@@ -0,0 +1,180 @@
1#define _POSIX_C_SOURCE 199309L
2#include <limits.h>
3#include <string.h>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <stdio.h>
7#include <poll.h>
8#include <time.h>
9#include <unistd.h>
10#include "list.h"
11#include "log.h"
12#include "loop.h"
13
14struct loop_fd_event {
15 void (*callback)(int fd, short mask, void *data);
16 void *data;
17};
18
19struct loop_timer {
20 void (*callback)(void *data);
21 void *data;
22 struct timespec expiry;
23};
24
25struct loop {
26 struct pollfd *fds;
27 int fd_length;
28 int fd_capacity;
29
30 list_t *fd_events; // struct loop_fd_event
31 list_t *timers; // struct loop_timer
32};
33
34struct loop *loop_create(void) {
35 struct loop *loop = calloc(1, sizeof(struct loop));
36 if (!loop) {
37 wlr_log(WLR_ERROR, "Unable to allocate memory for loop");
38 return NULL;
39 }
40 loop->fd_capacity = 10;
41 loop->fds = malloc(sizeof(struct pollfd) * loop->fd_capacity);
42 loop->fd_events = create_list();
43 loop->timers = create_list();
44 return loop;
45}
46
47void loop_destroy(struct loop *loop) {
48 list_foreach(loop->fd_events, free);
49 list_foreach(loop->timers, free);
50 list_free(loop->fd_events);
51 list_free(loop->timers);
52 free(loop->fds);
53 free(loop);
54}
55
56void loop_poll(struct loop *loop) {
57 // Calculate next timer in ms
58 int ms = INT_MAX;
59 if (loop->timers->length) {
60 struct timespec now;
61 clock_gettime(CLOCK_MONOTONIC, &now);
62 for (int i = 0; i < loop->timers->length; ++i) {
63 struct loop_timer *timer = loop->timers->items[i];
64 int timer_ms = (timer->expiry.tv_sec - now.tv_sec) * 1000;
65 timer_ms += (timer->expiry.tv_nsec - now.tv_nsec) / 1000000;
66 if (timer_ms < ms) {
67 ms = timer_ms;
68 }
69 }
70 }
71 if (ms < 0) {
72 ms = 0;
73 }
74
75 poll(loop->fds, loop->fd_length, ms);
76
77 // Dispatch fds
78 for (int i = 0; i < loop->fd_length; ++i) {
79 struct pollfd pfd = loop->fds[i];
80 struct loop_fd_event *event = loop->fd_events->items[i];
81
82 // Always send these events
83 unsigned events = pfd.events | POLLHUP | POLLERR;
84
85 if (pfd.revents & events) {
86 event->callback(pfd.fd, pfd.revents, event->data);
87 }
88 }
89
90 // Dispatch timers
91 if (loop->timers->length) {
92 struct timespec now;
93 clock_gettime(CLOCK_MONOTONIC, &now);
94 for (int i = 0; i < loop->timers->length; ++i) {
95 struct loop_timer *timer = loop->timers->items[i];
96 bool expired = timer->expiry.tv_sec < now.tv_sec ||
97 (timer->expiry.tv_sec == now.tv_sec &&
98 timer->expiry.tv_nsec < now.tv_nsec);
99 if (expired) {
100 timer->callback(timer->data);
101 loop_remove_timer(loop, timer);
102 --i;
103 }
104 }
105 }
106}
107
108void loop_add_fd(struct loop *loop, int fd, short mask,
109 void (*callback)(int fd, short mask, void *data), void *data) {
110 struct loop_fd_event *event = calloc(1, sizeof(struct loop_fd_event));
111 if (!event) {
112 wlr_log(WLR_ERROR, "Unable to allocate memory for event");
113 return;
114 }
115 event->callback = callback;
116 event->data = data;
117 list_add(loop->fd_events, event);
118
119 struct pollfd pfd = {fd, mask, 0};
120
121 if (loop->fd_length == loop->fd_capacity) {
122 loop->fd_capacity += 10;
123 loop->fds = realloc(loop->fds,
124 sizeof(struct pollfd) * loop->fd_capacity);
125 }
126
127 loop->fds[loop->fd_length++] = pfd;
128}
129
130struct loop_timer *loop_add_timer(struct loop *loop, int ms,
131 void (*callback)(void *data), void *data) {
132 struct loop_timer *timer = calloc(1, sizeof(struct loop_timer));
133 if (!timer) {
134 wlr_log(WLR_ERROR, "Unable to allocate memory for timer");
135 return NULL;
136 }
137 timer->callback = callback;
138 timer->data = data;
139
140 clock_gettime(CLOCK_MONOTONIC, &timer->expiry);
141 timer->expiry.tv_sec += ms / 1000;
142
143 long int nsec = (ms % 1000) * 1000000;
144 if (timer->expiry.tv_nsec + nsec >= 1000000000) {
145 timer->expiry.tv_sec++;
146 nsec -= 1000000000;
147 }
148 timer->expiry.tv_nsec += nsec;
149
150 list_add(loop->timers, timer);
151
152 return timer;
153}
154
155bool loop_remove_fd(struct loop *loop, int fd) {
156 for (int i = 0; i < loop->fd_length; ++i) {
157 if (loop->fds[i].fd == fd) {
158 free(loop->fd_events->items[i]);
159 list_del(loop->fd_events, i);
160
161 loop->fd_length--;
162 memmove(&loop->fds[i], &loop->fds[i + 1],
163 sizeof(struct pollfd) * (loop->fd_length - i));
164
165 return true;
166 }
167 }
168 return false;
169}
170
171bool loop_remove_timer(struct loop *loop, struct loop_timer *timer) {
172 for (int i = 0; i < loop->timers->length; ++i) {
173 if (loop->timers->items[i] == timer) {
174 list_del(loop->timers, i);
175 free(timer);
176 return true;
177 }
178 }
179 return false;
180}