fix identify_loops so that it actually identifies loops
[ajla.git] / iomux.c
blob44fe31926277ce3a84602392833dcf7b56f4fa45
1 /*
2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 #include "ajla.h"
21 #include "list.h"
22 #include "mem_al.h"
23 #include "thread.h"
24 #include "addrlock.h"
25 #include "rwlock.h"
26 #include "str.h"
27 #include "os.h"
28 #include "timer.h"
30 #include "iomux.h"
32 #ifdef IOMUX_SELECT
34 #include <unistd.h>
35 #ifdef HAVE_SYS_TIME_H
36 #include <sys/time.h>
37 #endif
38 #ifdef HAVE_SYS_SELECT_H
39 #include <sys/select.h>
40 #endif
42 struct iomux_wait {
43 struct list wait_list[2];
46 static void iomux_wait_init(struct iomux_wait *iow, handle_t attr_unused handle)
48 list_init(&iow->wait_list[0]);
49 list_init(&iow->wait_list[1]);
52 #include "iomux.inc"
54 void iomux_register_wait(handle_t handle, bool wr, mutex_t **mutex_to_lock, struct list *list_entry)
56 struct iomux_wait *iow = iomux_get_iowait(handle);
57 address_lock(iow, DEPTH_THUNK);
58 *mutex_to_lock = address_get_mutex(iow, DEPTH_THUNK);
59 list_add(&iow->wait_list[(int)wr], list_entry);
60 address_unlock(iow, DEPTH_THUNK);
61 #ifndef THREAD_NONE
62 os_notify();
63 #endif
67 #define handle_to_fd_set_size(h) (((size_t)(h) + FD_SETSIZE) / FD_SETSIZE * sizeof(fd_set))
69 bool iomux_test_handle(handle_t h, bool wr)
71 size_t size;
72 fd_set *fds;
73 struct timeval tv;
74 int r;
76 size = handle_to_fd_set_size(h);
77 fds = mem_calloc(fd_set *, size);
79 FD_SET(h, fds);
80 tv.tv_sec = 0;
81 tv.tv_usec = 0;
82 again:
83 EINTR_LOOP(r, select(h + 1, !wr ? fds : NULL, !wr ? NULL : fds, NULL, &tv));
84 if (unlikely(r == -1) && errno == EAGAIN)
85 goto again;
87 mem_free(fds);
89 return r > 0;
93 static fd_set *read_fd_set;
94 static fd_set *write_fd_set;
95 static size_t fd_set_size;
97 static void attr_cold fd_set_realloc(size_t new_size)
99 fd_set *f;
101 f = mem_realloc(fd_set *, read_fd_set, new_size);
102 memset((char *)f + fd_set_size, 0, new_size - fd_set_size);
103 read_fd_set = f;
105 f = mem_realloc(fd_set *, write_fd_set, new_size);
106 memset((char *)f + fd_set_size, 0, new_size - fd_set_size);
107 write_fd_set = f;
110 void iomux_check_all(uint32_t us)
112 size_t need_fd_set_size;
113 handle_t h, max_handle;
114 struct timeval tv;
115 int n;
117 rwlock_lock_read(&iomux_rwlock);
119 h = (handle_t)iowait_directory_size;
120 #ifdef OS_HAVE_NOTIFY_PIPE
121 if (os_notify_pipe[0] > h)
122 h = (size_t)os_notify_pipe[0];
123 #endif
125 need_fd_set_size = handle_to_fd_set_size(h);
126 if (unlikely(need_fd_set_size > fd_set_size))
127 fd_set_realloc(need_fd_set_size);
129 max_handle = 0;
130 for (h = 0; (size_t)h < iowait_directory_size; h++) {
131 struct iomux_wait *iow = iowait_directory[h];
132 if (iow) {
133 address_lock(iow, DEPTH_THUNK);
134 if (!list_is_empty(&iow->wait_list[0])) {
135 FD_SET(h, read_fd_set);
136 max_handle = h + 1;
138 if (!list_is_empty(&iow->wait_list[1])) {
139 FD_SET(h, write_fd_set);
140 max_handle = h + 1;
142 address_unlock(iow, DEPTH_THUNK);
146 rwlock_unlock_read(&iomux_rwlock);
148 #ifdef OS_HAVE_NOTIFY_PIPE
149 FD_SET(os_notify_pipe[0], read_fd_set);
150 if (os_notify_pipe[0] >= max_handle)
151 max_handle = os_notify_pipe[0] + 1;
152 #endif
154 us = iomux_get_time(us);
156 #if defined(OS_DOS)
157 if (dos_poll_devices()) {
158 n = 0;
159 } else if (!max_handle) {
160 if (us)
161 dos_yield();
162 n = 0;
163 } else {
164 tv.tv_sec = 0;
165 tv.tv_usec = 0;
166 n = select(max_handle, read_fd_set, write_fd_set, NULL, &tv);
167 if (!n && us)
168 dos_yield();
170 #else
171 if (us != IOMUX_INDEFINITE_WAIT) {
172 tv.tv_sec = us / 1000000;
173 tv.tv_usec = us % 1000000;
175 n = select(max_handle, read_fd_set, write_fd_set, NULL, us != IOMUX_INDEFINITE_WAIT ? &tv : NULL);
176 #endif
178 if (unlikely(n == -1)) {
179 int er = errno;
180 if (unlikely(er != EINTR) && unlikely(er != EAGAIN) && unlikely(er != EBADF))
181 fatal("select failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
182 if (unlikely(er == EBADF))
183 n = signed_maximum(int);
184 /* with n == -1, we are going to scan the whole array */
187 #ifdef OS_HAVE_NOTIFY_PIPE
188 if (FD_ISSET(os_notify_pipe[0], read_fd_set)) {
189 #ifdef THREAD_NONE
190 os_drain_notify_pipe();
191 #endif
192 FD_CLR(os_notify_pipe[0], read_fd_set);
193 n--;
195 #endif
198 * EMX has broken select. It should return the total number of bits set,
199 * but instead it returns the number of handles for which some activity
200 * is reported. Sometimes it returns unusually high values.
203 rwlock_lock_read(&iomux_rwlock);
205 for (h = 0; n && (size_t)h < iowait_directory_size; h++) {
206 if (FD_ISSET(h, read_fd_set)) {
207 if (n >= 0) {
208 struct iomux_wait *iow = iowait_directory[h];
209 address_lock(iow, DEPTH_THUNK);
210 call(wake_up_wait_list)(&iow->wait_list[0], address_get_mutex(iow, DEPTH_THUNK), true);
212 FD_CLR(h, read_fd_set);
213 #ifndef __EMX__
214 n--;
215 #endif
217 if (FD_ISSET(h, write_fd_set)) {
218 if (n >= 0) {
219 struct iomux_wait *iow = iowait_directory[h];
220 address_lock(iow, DEPTH_THUNK);
221 call(wake_up_wait_list)(&iow->wait_list[1], address_get_mutex(iow, DEPTH_THUNK), true);
223 FD_CLR(h, write_fd_set);
224 #ifndef __EMX__
225 n--;
226 #endif
230 rwlock_unlock_read(&iomux_rwlock);
234 void iomux_init(void)
236 rwlock_init(&iomux_rwlock);
237 array_init(struct iomux_wait *, &iowait_directory, &iowait_directory_size);
238 read_fd_set = mem_alloc(fd_set *, 0);
239 write_fd_set = mem_alloc(fd_set *, 0);
240 fd_set_size = 0;
241 #ifndef THREAD_NONE
242 thread_spawn(&iomux_thread, iomux_poll_thread, NULL, PRIORITY_IO, NULL);
243 #endif
246 void iomux_done(void)
248 size_t h;
249 os_shutdown_notify_pipe();
250 #ifndef THREAD_NONE
251 thread_join(&iomux_thread);
252 #endif
253 mem_free(read_fd_set);
254 mem_free(write_fd_set);
255 for (h = 0; h < iowait_directory_size; h++)
256 if (iowait_directory[h])
257 mem_free(iowait_directory[h]);
258 mem_free(iowait_directory);
259 rwlock_done(&iomux_rwlock);
262 #endif