Skip to content
Snippets Groups Projects
Commit 7a582646 authored by Ilya Verbin's avatar Ilya Verbin Committed by Vladimir Davydov
Browse files

core: allow spurious wakeups in coio_waitpid

Currently it's possible to wakeup a fiber, which is waiting for a
child process termination, using Tarantool C API. This will leave
a zombie process behind. This patch reworks `coio_waitpid` in such
a way that it yields until `cw.data` is set to NULL in the process
status change callback.

Part of #7166

NO_DOC=refactoring
NO_CHANGELOG=refactoring
parent 87e7d312
No related branches found
No related tags found
No related merge requests found
...@@ -491,25 +491,36 @@ coio_stat_stat_timeout(ev_stat *stat, ev_tstamp timeout) ...@@ -491,25 +491,36 @@ coio_stat_stat_timeout(ev_stat *stat, ev_tstamp timeout)
return 0; return 0;
} }
typedef void (*ev_child_cb)(ev_loop *, ev_child *, int); /**
* The process status change callback.
* Similar to fiber_schedule_cb, but also set watcher->data to NULL to indicate
* that the fiber has been woken up on the child process termination.
*/
static void
coio_status_change_cb(ev_loop *loop, ev_child *watcher, int revents)
{
(void)loop;
(void)revents;
struct fiber *fiber = watcher->data;
assert(fiber() == &cord()->sched);
watcher->data = NULL;
fiber_wakeup(fiber);
}
int int
coio_waitpid(pid_t pid, int *status) coio_waitpid(pid_t pid, int *status)
{ {
assert(cord_is_main()); assert(cord_is_main());
ev_child cw; ev_child cw;
ev_init(&cw, (ev_child_cb) fiber_schedule_cb); ev_init(&cw, coio_status_change_cb);
ev_child_set(&cw, pid, 0); ev_child_set(&cw, pid, 0);
cw.data = fiber(); cw.data = fiber();
ev_child_start(loop(), &cw); ev_child_start(loop(), &cw);
/*
* It's not safe to spuriously wakeup this fiber since do {
* in this case the server will leave a zombie process fiber_yield();
* behind. } while (cw.data != NULL);
*/
bool allow_cancel = fiber_set_cancellable(false);
fiber_yield();
fiber_set_cancellable(allow_cancel);
ev_child_stop(loop(), &cw); ev_child_stop(loop(), &cw);
if (fiber_is_cancelled()) { if (fiber_is_cancelled()) {
diag_set(FiberIsCancelled); diag_set(FiberIsCancelled);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/wait.h>
#include <sys/errno.h> #include <sys/errno.h>
int int
...@@ -171,6 +172,29 @@ test_writev_f(va_list ap) ...@@ -171,6 +172,29 @@ test_writev_f(va_list ap)
return 0; return 0;
} }
static int
test_waitpid_f(va_list ap)
{
/* Flush buffers to avoid multiple output. */
fflush(stdout);
fflush(stderr);
pid_t pid = fork();
/* Child process: do something for 10ms and exit. */
if (pid == 0) {
usleep(10000);
exit(0);
}
fail_if(pid == -1);
int status, rc = coio_waitpid(pid, &status);
fail_if(rc != 0);
fail_if(WIFEXITED(status) == 0);
return 0;
}
static void static void
fill_pipe(int fd) fill_pipe(int fd)
{ {
...@@ -211,11 +235,13 @@ read_write_test(void) ...@@ -211,11 +235,13 @@ read_write_test(void)
test_read_f, test_read_f,
test_write_f, test_write_f,
test_writev_f, test_writev_f,
test_waitpid_f
}; };
const char *descr[] = { const char *descr[] = {
"read", "read",
"write", "write",
"writev", "writev",
"waitpid"
}; };
int num_tests = sizeof(test_funcs) / sizeof(test_funcs[0]); int num_tests = sizeof(test_funcs) / sizeof(test_funcs[0]);
......
...@@ -20,11 +20,13 @@ ok 3 - bad ipv6 host name - error ...@@ -20,11 +20,13 @@ ok 3 - bad ipv6 host name - error
ok 4 - bad ipv6 host name - error message ok 4 - bad ipv6 host name - error message
*** test_connect: done *** *** test_connect: done ***
*** read_write_test *** *** read_write_test ***
1..6 1..8
ok 1 - coio_read handle spurious wakeup ok 1 - coio_read handle spurious wakeup
ok 2 - coio_read success after a spurious wakeup ok 2 - coio_read success after a spurious wakeup
ok 3 - coio_write handle spurious wakeup ok 3 - coio_write handle spurious wakeup
ok 4 - coio_write success after a spurious wakeup ok 4 - coio_write success after a spurious wakeup
ok 5 - coio_writev handle spurious wakeup ok 5 - coio_writev handle spurious wakeup
ok 6 - coio_writev success after a spurious wakeup ok 6 - coio_writev success after a spurious wakeup
ok 7 - coio_waitpid handle spurious wakeup
ok 8 - coio_waitpid success after a spurious wakeup
*** read_write_test: done *** *** read_write_test: done ***
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment