26 #if _POSIX_C_SOURCE >= 200112L 48 #include <sys/eventfd.h> 53 static void io_fd_loop_std_exec_impl_on_task_init(ev_std_exec_impl_t *impl);
54 static void io_fd_loop_std_exec_impl_on_task_fini(ev_std_exec_impl_t *impl);
55 static void io_fd_loop_std_exec_impl_post(
56 ev_std_exec_impl_t *impl,
struct ev_task *task);
57 static size_t io_fd_loop_std_exec_impl_abort(
58 ev_std_exec_impl_t *impl,
struct ev_task *task);
62 &io_fd_loop_std_exec_impl_on_task_init,
63 &io_fd_loop_std_exec_impl_on_task_fini,
64 &io_fd_loop_std_exec_impl_post,
65 &io_fd_loop_std_exec_impl_abort
70 static void io_fd_loop_svc_shutdown(
struct io_svc *svc);
73 static const struct io_svc_vtbl io_fd_loop_svc_vtbl = {
74 &io_fd_loop_svc_notify_fork,
75 &io_fd_loop_svc_shutdown
109 unsigned shutdown : 1;
110 unsigned stopped : 1;
111 unsigned running : 1;
118 #if LELY_NO_THREADS || LELY_NO_ATOMICS 128 const ev_std_exec_impl_t *impl);
141 io_fd_loop_alloc(
void)
147 io_fd_loop_free(
void *ptr)
166 loop->
impl_vptr = &io_fd_loop_std_exec_impl_vtbl;
170 &io_fd_loop_watch_func);
172 loop->
fd[1] = loop->
fd[0] = -1;
175 if ((errsv = pthread_mutex_init(&loop->
mtx, NULL)))
183 #if LELY_NO_THREADS || LELY_NO_ATOMICS 186 atomic_init(&loop->
ntasks, 0);
190 if (io_fd_loop_open(loop) == -1) {
202 pthread_mutex_destroy(&loop->
mtx);
216 io_fd_loop_close(loop);
219 pthread_mutex_destroy(&loop->
mtx);
244 io_fd_loop_free(loop);
254 io_fd_loop_fini(loop);
255 io_fd_loop_free(loop);
272 return &loop->
exec.exec_vptr;
289 while (pthread_mutex_lock(&loop->
mtx) == EINTR)
294 pthread_mutex_unlock(&loop->
mtx);
302 while (pthread_mutex_lock(&loop->
mtx) == EINTR)
305 int stopped = loop->stopped;
307 pthread_mutex_unlock(&loop->
mtx);
318 while (pthread_mutex_lock(&loop->
mtx) == EINTR)
323 pthread_mutex_unlock(&loop->
mtx);
331 while (io_fd_loop_run_one(loop))
342 while (pthread_mutex_lock(&loop->
mtx) == EINTR)
345 struct ev_task *task = io_fd_loop_do_pop(loop);
347 pthread_mutex_unlock(&loop->
mtx);
359 io_fd_loop_std_exec_impl_on_task_init(ev_std_exec_impl_t *impl)
363 #if LELY_NO_THREADS || LELY_NO_ATOMICS 366 atomic_fetch_add_explicit(&loop->
ntasks, 1, memory_order_relaxed);
371 io_fd_loop_std_exec_impl_on_task_fini(ev_std_exec_impl_t *impl)
375 #if LELY_NO_THREADS || LELY_NO_ATOMICS 378 if (atomic_fetch_sub_explicit(&loop->
ntasks, 1, memory_order_release)
380 atomic_thread_fence(memory_order_acquire);
383 while (pthread_mutex_lock(&loop->
mtx) == EINTR)
389 pthread_mutex_unlock(&loop->
mtx);
395 io_fd_loop_std_exec_impl_post(ev_std_exec_impl_t *impl,
struct ev_task *task)
401 while (pthread_mutex_lock(&loop->
mtx) == EINTR)
405 int running = !loop->shutdown && loop->running;
407 pthread_mutex_unlock(&loop->
mtx);
412 if (io_fd_loop_write(loop) == -1)
418 io_fd_loop_std_exec_impl_abort(ev_std_exec_impl_t *impl,
struct ev_task *task)
426 while (pthread_mutex_lock(&loop->
mtx) == EINTR)
434 pthread_mutex_unlock(&loop->
mtx);
454 if (io_fd_loop_close(loop) == -1 && !result) {
459 if (io_fd_loop_open(loop) == -1 && !result) {
475 io_fd_loop_svc_shutdown(
struct io_svc *svc)
480 while (pthread_mutex_lock(&loop->
mtx) == EINTR)
483 if (!loop->shutdown) {
489 pthread_mutex_unlock(&loop->
mtx);
494 io_fd_loop_from_impl(
const ev_std_exec_impl_t *impl)
502 io_fd_loop_from_svc(
const struct io_svc *svc)
518 io_fd_loop_read(loop);
521 while (pthread_mutex_lock(&loop->
mtx) == EINTR)
524 assert(!loop->running);
527 while (!loop->shutdown && (task = io_fd_loop_do_pop(loop))) {
529 pthread_mutex_unlock(&loop->
mtx);
534 while (pthread_mutex_lock(&loop->
mtx) == EINTR)
542 pthread_mutex_unlock(&loop->
mtx);
558 #if LELY_NO_THREADS || LELY_NO_ATOMICS 559 if (!task && !loop->
ntasks)
562 if (!task && !atomic_load_explicit((atomic_size_t *)&loop->
ntasks,
563 memory_order_relaxed))
577 if (io_fd_loop_close(loop) == -1) {
583 loop->
fd[1] = loop->
fd[0] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
589 if (pipe(loop->
fd) == -1) {
597 goto error_set_cloexec;
603 goto error_set_nonblock;
610 goto error_poll_watch;
626 loop->
fd[1] = loop->
fd[0] = -1;
637 if (loop->
fd[0] == -1)
645 &loop->
watch) == -1 && !result) {
652 if (close(loop->
fd[1]) == -1 && !result) {
658 if (close(loop->
fd[0]) == -1 && !result) {
663 loop->
fd[1] = loop->
fd[0] = -1;
682 ssize_t result = read(loop->
fd[0], &buf,
sizeof(buf));
683 if (result < 0 && errno != EINTR) {
684 if (errno != EAGAIN || errno != EWOULDBLOCK)
706 result = write(loop->
fd[1], &buf,
sizeof(buf));
707 }
while (result < 0 && errno == EINTR);
708 if (result < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
711 return result > 0 ? 1 : 0;
714 #endif // _POSIX_C_SOURCE >= 200112L int fd[2]
The file descriptor corresponding to the event loop.
This is the internal header file of the POSIX-specific I/O declarations.
size_t io_fd_loop_run(io_fd_loop_t *loop)
Equivalent to size_t n = 0; while (io_fd_loop_run_one(loop)) n += n < SIZE_MAX; return n;...
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
ev_poll_t * io_poll_get_poll(const io_poll_t *poll)
Returns a pointer to the ev_poll_t instance corresponding to the I/O polling instance.
This header file is part of the event library; it contains the standard executor declarations.
ev_poll_t * io_fd_loop_get_poll(const io_fd_loop_t *loop)
Returns a pointer to the polling instance used by the event loop.
An I/O polling interface.
io_fd_loop_t * io_fd_loop_create(io_poll_t *poll)
Creates a new file descriptor event loop.
Data (other than priority data) MAY be read without blocking.
The event generated after the fork in the child process.
struct ev_std_exec exec
The executor corresponding to the event loop.
int io_fd_set_cloexec(int fd)
Sets the FD_CLOEXEC flag of the file descriptor fd.
io_fork_event
The type of event generated by an I/O context before and after a process fork.
pthread_mutex_t mtx
The mutex protecting the task queue.
void io_ctx_insert(io_ctx_t *ctx, struct io_svc *svc)
Registers an I/O service with an I/O context.
atomic_size_t ntasks
The number of pending tasks.
int io_fd_loop_get_fd(const io_fd_loop_t *loop)
Returns the file descriptor corresponding to the event loop.
A file descriptor event loop.
struct slnode * sllist_pop_front(struct sllist *list)
Pops a node from the front of a singly-linked list.
struct sllist * sllist_append(struct sllist *dst, struct sllist *src)
Appends the singly-linked list at src to the one at dst.
int io_fd_loop_stopped(io_fd_loop_t *loop)
Returns 1 if the file descriptor event loop is stopped, and 0 if not.
io_poll_t * poll
A pointer to the I/O polling instance used to monitor the event loop.
struct slnode * sllist_remove(struct sllist *list, struct slnode *node)
Removes a node from a singly-linked list.
void sllist_push_back(struct sllist *list, struct slnode *node)
Pushes a node to the back of a singly-linked list.
void io_fd_loop_stop(io_fd_loop_t *loop)
Stops the file descriptor event loop.
ev_exec_t * exec
A pointer to the executor to which the task is (to be) submitted.
This header file is part of the C11 and POSIX compatibility library; it includes <stdatomic.h>, if it exists, and defines any missing functionality.
This header file is part of the C11 and POSIX compatibility library; it includes <unistd.h>, if it exists, and defines any missing functionality.
struct io_poll_watch watch
The object used to monitor the file descriptor for I/O events.
#define IO_POLL_WATCH_INIT(func)
The static initializer for io_poll_watch.
This is the internal header file of the common file descriptor functions.
const struct ev_poll_vtbl *const ev_poll_t
The abstract polling interface.
ev_exec_t * io_fd_loop_get_exec(const io_fd_loop_t *loop)
Returns a pointer to the executor corresponding to the event loop.
void io_fd_loop_restart(io_fd_loop_t *loop)
Restarts a file descriptor event loop.
void io_fd_loop_destroy(io_fd_loop_t *loop)
Destroys a file descriptor event loop.
This header file is part of the event library; it contains the file descriptor event loop declaration...
int io_poll_watch(io_poll_t *poll, io_handle_t handle, struct io_event *event, int keep)
Registers an I/O device with an I/O polling interface and instructs it to watch for certain events...
const struct ev_std_exec_impl_vtbl * impl_vptr
A pointer to the virtual table containing the interface used by the standard executor (exec)...
void io_ctx_remove(io_ctx_t *ctx, struct io_svc *svc)
Unregisters an I/O service with an I/O context.
An object representing a file descriptor being monitored for I/O events.
This header file is part of the C11 and POSIX compatibility library; it includes <stdint.h> and defines any missing functionality.
io_ctx_t * io_poll_get_ctx(const io_poll_t *poll)
Returns a pointer to the I/O context with which the I/O polling instance is registered.
This header file is part of the event library; it contains the task declarations. ...
int sllist_empty(const struct sllist *list)
Returns 1 if the singly-linked list is empty, and 0 if not.
#define structof(ptr, type, member)
Obtains the address of a structure from the address of one of its members.
The virtual table of an I/O service.
#define IO_SVC_INIT(vptr)
The static initializer for io_svc.
struct ev_task * ev_task_from_node(struct slnode *node)
Converts a pointer to a node in a queue to the address of the task containing the node...
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib.h> and defines any missing functionality.
This header file is part of the utilities library; it contains the singly-linked list declarations...
int io_fd_set_nonblock(int fd)
Sets the O_NONBLOCK flag of the file descriptor fd.
void sllist_init(struct sllist *list)
Initializes a singly-linked list.
io_ctx_t * ctx
A pointer to the I/O context with which the event loop is registered.
void ev_exec_run(ev_exec_t *exec, struct ev_task *task)
Invokes the task function in *task as if the task is being executed by *exec.
This is the public header file of the utilities library.
struct io_svc svc
The I/O service representing the event loop.
struct sllist queue
The queue of pending tasks.