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
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/>.
35 #ifdef HAVE_SYS_TIME_H
38 #ifdef HAVE_SYS_SELECT_H
39 #include <sys/select.h>
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]);
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
);
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
)
76 size
= handle_to_fd_set_size(h
);
77 fds
= mem_calloc(fd_set
*, size
);
83 EINTR_LOOP(r
, select(h
+ 1, !wr
? fds
: NULL
, !wr
? NULL
: fds
, NULL
, &tv
));
84 if (unlikely(r
== -1) && errno
== EAGAIN
)
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
)
101 f
= mem_realloc(fd_set
*, read_fd_set
, new_size
);
102 memset((char *)f
+ fd_set_size
, 0, new_size
- fd_set_size
);
105 f
= mem_realloc(fd_set
*, write_fd_set
, new_size
);
106 memset((char *)f
+ fd_set_size
, 0, new_size
- fd_set_size
);
110 void iomux_check_all(uint32_t us
)
112 size_t need_fd_set_size
;
113 handle_t h
, max_handle
;
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];
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
);
130 for (h
= 0; (size_t)h
< iowait_directory_size
; h
++) {
131 struct iomux_wait
*iow
= iowait_directory
[h
];
133 address_lock(iow
, DEPTH_THUNK
);
134 if (!list_is_empty(&iow
->wait_list
[0])) {
135 FD_SET(h
, read_fd_set
);
138 if (!list_is_empty(&iow
->wait_list
[1])) {
139 FD_SET(h
, write_fd_set
);
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;
154 us
= iomux_get_time(us
);
157 if (dos_poll_devices()) {
159 } else if (!max_handle
) {
166 n
= select(max_handle
, read_fd_set
, write_fd_set
, NULL
, &tv
);
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
);
178 if (unlikely(n
== -1)) {
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
)) {
190 os_drain_notify_pipe();
192 FD_CLR(os_notify_pipe
[0], read_fd_set
);
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
)) {
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
);
217 if (FD_ISSET(h
, write_fd_set
)) {
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
);
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);
242 thread_spawn(&iomux_thread
, iomux_poll_thread
, NULL
, PRIORITY_IO
, NULL
);
246 void iomux_done(void)
249 os_shutdown_notify_pipe();
251 thread_join(&iomux_thread
);
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
);