1 #include <sys/syscall.h>
2 #include <sys/time.h>
3 #include <time.h>
4 #include <unistd.h>
5
6 #include <atomic>
7 #include <csignal>
8 #include <string>
9
10 #include "absl/base/log_severity.h"
11 #include "absl/flags/flag.h"
12 #include "absl/flags/parse.h"
13 #include "absl/log/check.h"
14 #include "absl/log/globals.h"
15 #include "absl/log/initialize.h"
16 #include "absl/log/log.h"
17 #include "absl/strings/string_view.h"
18 #include "absl/time/clock.h"
19 #include "absl/time/time.h"
20
21 ABSL_FLAG(std::string, sigev_notify_kind, "",
22 "The C name for the kind of POSIX timer to create (sigev_notify), or "
23 "\"syscall(SIGEV_THREAD)\" for a manual syscall approach which "
24 "checks that no threads were created.");
25
main(int argc,char * argv[])26 int main(int argc, char* argv[]) {
27 absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo);
28 absl::ParseCommandLine(argc, argv);
29 absl::InitializeLog();
30
31 static std::atomic<bool> timer_expired(false);
32 static std::atomic<pid_t> tid(0);
33 static_assert(std::atomic<pid_t>::is_always_lock_free);
34 // Handle SIGPROF by recording that it arrived.
35 signal(
36 SIGPROF, +[](int) {
37 timer_expired.store(true);
38 tid.store(syscall(__NR_gettid));
39 });
40
41 const std::string sigev_notify_kind = absl::GetFlag(FLAGS_sigev_notify_kind);
42 struct sigevent sev {};
43 sev.sigev_signo = SIGPROF;
44
45 if (sigev_notify_kind == "SIGEV_THREAD" ||
46 sigev_notify_kind == "syscall(SIGEV_THREAD)") {
47 sev.sigev_notify = SIGEV_THREAD;
48 sev.sigev_notify_function = +[](sigval_t) {
49 timer_expired.store(true);
50 tid.store(syscall(__NR_gettid));
51 };
52 } else if (sigev_notify_kind == "SIGEV_SIGNAL") {
53 sev.sigev_notify = SIGEV_SIGNAL;
54 sev.sigev_signo = SIGPROF;
55 } else if (sigev_notify_kind == "SIGEV_NONE") {
56 sev.sigev_notify = SIGEV_NONE;
57 } else if (sigev_notify_kind == "SIGEV_THREAD_ID") {
58 sev.sigev_notify = SIGEV_THREAD_ID;
59 sev.sigev_signo = SIGPROF;
60 #ifndef sigev_notify_thread_id
61 sev._sigev_un._tid = syscall(__NR_gettid);
62 #else
63 sev.sigev_notify_thread_id = syscall(__NR_gettid);
64 #endif
65 } else {
66 LOG(QFATAL) << "Invalid --sigev_notify_kind: " << sigev_notify_kind;
67 }
68
69 struct itimerspec timerspec {};
70 timerspec.it_interval.tv_sec = 0;
71 timerspec.it_interval.tv_nsec = 1'000'000;
72 timerspec.it_value.tv_sec = 0;
73 timerspec.it_value.tv_nsec = 1'000'000;
74
75 if (sigev_notify_kind == "syscall(SIGEV_THREAD)") {
76 // Use raw syscalls.
77 int timer;
78 PCHECK(syscall(__NR_timer_create, CLOCK_REALTIME, &sev, &timer) == 0);
79 PCHECK(syscall(__NR_timer_settime, timer, 0, &timerspec, nullptr) == 0);
80
81 // Long enough to effectively guarantee that we see the notification.
82 absl::SleepFor(absl::Milliseconds(30));
83
84 PCHECK(syscall(__NR_timer_gettime, timer, &timerspec) == 0);
85 PCHECK(syscall(__NR_timer_getoverrun, timer) != -1);
86
87 PCHECK(syscall(__NR_timer_delete, timer) == 0);
88
89 // The syscall with SIGEV_THREAD doesn't spawn a thread, which we can verify
90 // by checking that the thread ID is the main thread.
91 CHECK_EQ(tid.load(), syscall(__NR_gettid));
92 } else {
93 timer_t timer;
94 PCHECK(timer_create(CLOCK_REALTIME, &sev, &timer) == 0);
95 PCHECK(timer_settime(timer, 0, &timerspec, nullptr) == 0);
96
97 // Long enough to effectively guarantee that we see the notification.
98 absl::SleepFor(absl::Milliseconds(30));
99
100 PCHECK(timer_gettime(timer, &timerspec) == 0);
101 PCHECK(timer_getoverrun(timer) != -1);
102
103 PCHECK(timer_delete(timer) == 0);
104 }
105
106 if (sigev_notify_kind == "SIGEV_THREAD" ||
107 sigev_notify_kind == "syscall(SIGEV_THREAD)" ||
108 sigev_notify_kind == "SIGEV_THREAD_ID" ||
109 sigev_notify_kind == "SIGEV_SIGNAL") {
110 CHECK(timer_expired.load());
111 } else {
112 CHECK_EQ(sigev_notify_kind, "SIGEV_NONE");
113 CHECK(!timer_expired.load());
114 }
115
116 return 0;
117 }
118