diff --git a/ext/event/backend/epoll.c b/ext/event/backend/epoll.c index b1573e38..a9be9349 100644 --- a/ext/event/backend/epoll.c +++ b/ext/event/backend/epoll.c @@ -27,6 +27,8 @@ #include #include +#include "pidfd.c" + static VALUE Event_Backend_EPoll = Qnil; static ID id_fileno; @@ -113,6 +115,60 @@ VALUE Event_Backend_EPoll_close(VALUE self) { return Qnil; } +struct process_wait_arguments { + struct Event_Backend_EPoll *data; + pid_t pid; + int flags; + int descriptor; +}; + +static +VALUE process_wait_transfer(VALUE _arguments) { + struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments; + + Event_Backend_transfer(arguments->data->loop); + + return Event_Backend_process_status_wait(arguments->pid); +} + +static +VALUE process_wait_ensure(VALUE _arguments) { + struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments; + + // epoll_ctl(arguments->data->descriptor, EPOLL_CTL_DEL, arguments->descriptor, NULL); + + close(arguments->descriptor); + + return Qnil; +} + +VALUE Event_Backend_EPoll_process_wait(VALUE self, VALUE fiber, VALUE pid, VALUE flags) { + struct Event_Backend_EPoll *data = NULL; + TypedData_Get_Struct(self, struct Event_Backend_EPoll, &Event_Backend_EPoll_Type, data); + + struct process_wait_arguments process_wait_arguments = { + .data = data, + .pid = NUM2PIDT(pid), + .flags = NUM2INT(flags), + }; + + process_wait_arguments.descriptor = pidfd_open(process_wait_arguments.pid, 0); + rb_update_max_fd(process_wait_arguments.descriptor); + + struct epoll_event event = { + .events = EPOLLIN|EPOLLRDHUP|EPOLLONESHOT, + .data = {.ptr = (void*)fiber}, + }; + + int result = epoll_ctl(data->descriptor, EPOLL_CTL_ADD, process_wait_arguments.descriptor, &event); + + if (result == -1) { + rb_sys_fail("epoll_ctl(process_wait)"); + } + + return rb_ensure(process_wait_transfer, (VALUE)&process_wait_arguments, process_wait_ensure, (VALUE)&process_wait_arguments); +} + static inline uint32_t epoll_flags_from_events(int events) { uint32_t flags = 0; @@ -296,61 +352,6 @@ VALUE Event_Backend_EPoll_select(VALUE self, VALUE duration) { return INT2NUM(arguments.count); } -VALUE Event_Backend_EPoll_process_wait(VALUE self, VALUE fiber, VALUE pid, VALUE flags) { - pid_t pidv = NUM2PIDT(pid); - int options = NUM2INT(flags); - int state = 0; - int err = 0; - - if ((flags & WNOHANG) > 0) { - // WNOHANG is nonblock by default. - pid_t ret = PIDT2NUM(waitpid(pidv, &state, options)); - if (ret == -1) err = errno; - return Event_Backend_process_status(pidv, state, err); - } - - struct Event_Backend_EPoll *data = NULL; - TypedData_Get_Struct(self, struct Event_Backend_EPoll, &Event_Backend_EPoll_Type, data); - - struct epoll_event event = {0}; - - int descriptor = pidfd_open(pidv, 0); - int duplicate = -1; - - event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT; - event.data.ptr = (void*)fiber; - - // A better approach is to batch all changes: - int result = epoll_ctl(data->descriptor, EPOLL_CTL_ADD, descriptor, &event); - - if (result == -1 && errno == EEXIST) { - // The file descriptor was already inserted into epoll. - duplicate = descriptor = dup(descriptor); - - rb_update_max_fd(duplicate); - - if (descriptor == -1) - rb_sys_fail("dup"); - - result = epoll_ctl(data->descriptor, EPOLL_CTL_ADD, descriptor, &event); - } - - if (result == -1) { - rb_sys_fail("epoll_ctl"); - } - - struct io_wait_arguments io_wait_arguments = { - .data = data, - .descriptor = descriptor, - .duplicate = duplicate - }; - - rb_ensure(io_wait_transfer, (VALUE)&io_wait_arguments, io_wait_ensure, (VALUE)&io_wait_arguments); - pid_t ret = PIDT2NUM(waitpid(pidv, &state, options)); - if (ret == -1) err = errno; - return Event_Backend_process_status(pidv, state, err); -} - void Init_Event_Backend_EPoll(VALUE Event_Backend) { id_fileno = rb_intern("fileno"); diff --git a/ext/event/backend/pidfd.c b/ext/event/backend/pidfd.c new file mode 100644 index 00000000..ba73a3a4 --- /dev/null +++ b/ext/event/backend/pidfd.c @@ -0,0 +1,36 @@ +// Copyright, 2021, by Samuel G. D. Williams. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include +#include +#include +#include +#include +#include + +#ifndef __NR_pidfd_open +#define __NR_pidfd_open 434 /* System call # on most architectures */ +#endif + +static int +pidfd_open(pid_t pid, unsigned int flags) +{ + return syscall(__NR_pidfd_open, pid, flags); +}