diff --git a/README.md b/README.md index f4c4bce..4802346 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ for Linux, written on the kernel's own system-call interface. openkal = "0.17.0" [target.'cfg(os = "linux")'.dependencies] -openkal-linux = "0.18.1" +openkal-linux = "0.18.2" ``` ## Why it does not use a C library diff --git a/mcpp.toml b/mcpp.toml index f3bfdcb..b1a35eb 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-linux" -version = "0.18.1" +version = "0.18.2" description = "The reference implementation of openkal for Linux, written on the kernel's own system-call interface so that it can be placed beneath a C library as well as above one." license = "Apache-2.0" diff --git a/src/process.cpp b/src/process.cpp index 70f9c90..ac2cb80 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -929,8 +929,12 @@ void stop_handler(int signo) { __atomic_compare_exchange_n(&g_stop_signal, &none, signo, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED); __atomic_store_n(&g_stop_word, 1u, __ATOMIC_RELEASE); + // PRIVATE, AS `kal_task_wait' WAITS (0.18.2). The kernel keys a private + // futex apart from a shared one at the same address, so a shared wake + // reached no context waiting through `kal_task_wait' with no bound, and a + // program that waits for its end to be requested never ended. okl::sys(okl::nr_futex, reinterpret_cast(&g_stop_word), - 1 /* FUTEX_WAKE */, 0x7fffffff, 0, 0, 0); + okl::futex_wake | okl::futex_private, 0x7fffffff, 0, 0, 0); } // THE RESULT IS EXAMINED, AND IT WAS NOT WHEN THIS SHIPPED IN 0.11. An diff --git a/tests/stop_word_take_over.cpp b/tests/stop_word_take_over.cpp index a515108..6a4d05e 100644 --- a/tests/stop_word_take_over.cpp +++ b/tests/stop_word_take_over.cpp @@ -1,13 +1,18 @@ // The stop word, taken over and given back (openkal 0.17, process.h; K1, K2). // -// Three facts, each of which this implementation got wrong before 0.18: +// Four facts, each of which this implementation got wrong once: // 1. a request delivered after the word is armed returns from the handler --- // under GCC -O0 the return trampoline was in `.bss' and the program died // with SIGSEGV on its way back (K1); // 2. a request the starter arranged not to deliver stays undelivered: SIGINT // ignored before arming is still ignored after it (K2); // 3. ending as requested ends the program as the request would have, which a -// started copy reports to its starter as a signal and not as a status (G4). +// started copy reports to its starter as a signal and not as a status (G4); +// 4. (0.18.2) a context waiting upon the word with `kal_task_wait' and no +// bound is woken by the request. The handler woke the word as a shared +// futex and `kal_task_wait' waits on it as a private one, which the kernel +// keys apart: the waiter slept on, and a program that ends at a request by +// waiting for it never ended. #include #include #include @@ -24,7 +29,30 @@ void check(bool held, const char* what) { } } +struct waiter { + const kal_u32* word; + volatile int woken; +}; +void wait_for_request(void* p) { + auto* w = static_cast(p); + while (*w->word == 0) kal_task_wait(w->word, 0, 0); // no bound + w->woken = 1; +} + int main(int argc, char** argv) { + if (argc > 1 && std::strcmp(argv[1], "--waiter") == 0) { + const kal_u32* word = kal_process_stop_requested(); + if (word == nullptr) return 90; + waiter w{ word, 0 }; + kal_task t{}; + if (kal_task_start(wait_for_request, &w, &t) != kal_ok) return 94; + ::usleep(100000); // the context is waiting by now + ::kill(::getpid(), SIGTERM); + for (int i = 0; i < 2000 && !w.woken; ++i) ::usleep(1000); + if (!w.woken) return 95; + kal_task_join(t); + return 0; + } if (argc > 1 && std::strcmp(argv[1], "--hang-up") == 0) { // 0.18.1: the closing of the terminal is a request to end, taken over // with the others, and the program ends as it would have. @@ -79,6 +107,16 @@ int main(int argc, char** argv) { check(WIFSIGNALED(st) && WTERMSIG(st) == SIGHUP, "a hang-up is taken over as a request, and the program ends as it would have"); + const pid_t waiting = ::fork(); + if (waiting == 0) { + ::execl("/proc/self/exe", argv[0], "--waiter", nullptr); + ::_exit(93); + } + st = 0; + ::waitpid(waiting, &st, 0); + check(WIFEXITED(st) && WEXITSTATUS(st) == 0, + "a context waiting upon the word with no bound is woken by the request"); + if (failures == 0) std::printf("openkal-linux: the stop word is taken over and given back\n"); return failures == 0 ? 0 : 1; }