Repository navigation
fix(logger): decide colors without a static guard a fork child inherits #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
610446e
4edb259
01fe73f
f339a83
84d6819
a0c2f45
4d43094
dce596a
2c73405
1be612d
9c8a615
008823b
d63a184
2e82a6d
d6f58a0
73c9198
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [important]
General fix:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 9c8a615. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // isatty() for test_fork_color.cpp, linked into the test program ahead of the | ||
| // C library. The logger calls it to decide whether stderr takes colors. The | ||
| // first call says stderr is a terminal, after long enough for the test to fork | ||
| // and to decide in another thread during it; later calls say it is not, at | ||
| // once. | ||
|
|
||
| #include <atomic> | ||
| #include <time.h> | ||
| #include <unistd.h> | ||
|
|
||
| std::atomic<bool> g_deciding_colors{false}; | ||
| std::atomic<int> g_isatty_calls{0}; | ||
|
|
||
| // Same exception specification as the C library's declaration, which glibc | ||
| // marks noexcept. | ||
| extern "C" int isatty(int) noexcept(noexcept(::isatty(0))) { | ||
| if (g_isatty_calls.fetch_add(1) != 0) | ||
| return 0; | ||
| g_deciding_colors.store(true); | ||
| const timespec delay{0, 300 * 1000 * 1000}; | ||
| nanosleep(&delay, nullptr); | ||
| return 1; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| #ifndef CORETRACE_LOGGER_TESTS_FORK_HELPERS_HPP | ||
| #define CORETRACE_LOGGER_TESTS_FORK_HELPERS_HPP | ||
|
|
||
| // Helpers shared by the POSIX tests that fork. They need the default SIGCHLD | ||
| // action, which each test sets: when SIGCHLD is ignored, waitpid() cannot reap | ||
| // a child. | ||
|
|
||
| #include <atomic> | ||
| #include <chrono> | ||
| #include <csignal> | ||
| #include <cstdio> | ||
| #include <sys/wait.h> | ||
| #include <thread> | ||
| #include <unistd.h> | ||
|
|
||
| namespace ct_test { | ||
|
|
||
| inline constexpr std::chrono::seconds kForkTimeout{5}; | ||
|
|
||
| /// True when `child` exits with status 0. A child still running after | ||
| /// kForkTimeout is killed, and reported as hung. | ||
| [[nodiscard]] inline bool child_succeeded(pid_t child) { | ||
| if (child <= 0) | ||
| return false; | ||
| int status = 0; | ||
| const auto deadline = std::chrono::steady_clock::now() + kForkTimeout; | ||
| while (std::chrono::steady_clock::now() < deadline) { | ||
| if (waitpid(child, &status, WNOHANG) == child) | ||
| return WIFEXITED(status) && WEXITSTATUS(status) == 0; | ||
| usleep(1000); | ||
| } | ||
| kill(child, SIGKILL); | ||
| waitpid(child, &status, 0); | ||
| std::fprintf(stderr, "child %d hung: killed after %lld s\n", | ||
| static_cast<int>(child), | ||
| static_cast<long long>(kForkTimeout.count())); | ||
| return false; | ||
| } | ||
|
|
||
| /// True once `flag` is set, false after kForkTimeout. | ||
| [[nodiscard]] inline bool wait_for(const std::atomic<bool> &flag) { | ||
| const auto deadline = std::chrono::steady_clock::now() + kForkTimeout; | ||
| while (!flag.load()) { | ||
| if (std::chrono::steady_clock::now() >= deadline) | ||
| return false; | ||
| std::this_thread::yield(); | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| } // namespace ct_test | ||
|
|
||
| #endif // CORETRACE_LOGGER_TESTS_FORK_HELPERS_HPP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[blocking]
pid()(line 597) keeps the static guard this PR removes fromuse_color().It is on the same first-line path (
write_log_line(),write_prefix()), so a child forked while another thread runs that initializer waits for its guard forever, exactly like the color decision. Independently of the race, every line of a child carries the parent's pid (the PR description mentions it).Fix:
return platform::process_id();.getpid()is cheap next to a log line.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 1be612d:
pid()returnsplatform::process_id().test_fork.cpp(2c73405) checks that a child'spid()equals itsgetpid().