GCC Code Coverage Report


Directory: include/tts/
File: include/tts/engine/watchdog.hpp
Date: 2026-10-14 16:28:14
Exec Total Coverage
Lines: 9 33 27.3%
Functions: 3 5 60.0%
Branches: 6 15 40.0%

Line Branch Exec Source
1 //======================================================================================================================
2 //! @file
3 /*
4 TTS - Tiny Test System
5 Copyright : TTS Contributors & Maintainers
6 SPDX-License-Identifier: BSL-1.0
7 */
8 //======================================================================================================================
9 #pragma once
10
11 #include <tts/engine/abort.hpp>
12 #include <tts/engine/abort_report.hpp>
13 #include <tts/engine/environment.hpp>
14 #include <tts/engine/test.hpp>
15
16 #if defined(__EMSCRIPTEN__)
17 #elif defined(_WIN32)
18 #define WIN32_LEAN_AND_MEAN
19 #define NOMINMAX
20 #include <windows.h>
21 #else
22 #include <csignal>
23 #include <sys/time.h>
24 #endif
25
26 namespace tts::_
27 {
28 inline nanoseconds watchdog_ns = 0; // NOSONAR - the loop sets it before each case
29
30 282 inline nanoseconds default_timeout_ns()
31 {
32
4/7
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 188 times.
✓ Branch 3 taken 94 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 94 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
282 static nanoseconds that = ::tts::arguments().value<unsigned long long>("--timeout") * 1'000'000;
33 282 return that;
34 }
35
36 ✗ inline void report_timeout()
37 {
38 ::tts::text line {"TEST: '%s' - @@ TIMEOUT @@ still running after %s",
39 current_test,
40 ✗ format_duration(static_cast<double>(watchdog_ns)).data()};
41
42 ✗ report_abort(line.data(), "TIMEOUT");
43 ✗ }
44 }
45
46 #if defined(__EMSCRIPTEN__)
47 namespace tts::_
48 {
49 // No process-wide timer here: nothing to arm.
50 struct watchdog
51 {
52 explicit watchdog(nanoseconds)
53 {
54 }
55 };
56 }
57 #elif defined(_WIN32)
58 namespace tts::_
59 {
60 inline VOID CALLBACK watchdog_expired(PVOID, BOOLEAN)
61 {
62 static bool reporting = false;
63 if(reporting) exit_now();
64 reporting = true;
65
66 report_timeout();
67 perform_abort(0);
68 }
69
70 struct watchdog
71 {
72 explicit watchdog(nanoseconds ns)
73 {
74 if(!ns) return;
75
76 watchdog_ns = ns;
77 CreateTimerQueueTimer(&timer_,
78 nullptr,
79 &watchdog_expired,
80 nullptr,
81 static_cast<DWORD>(ns / 1'000'000u),
82 0,
83 WT_EXECUTEINTIMERTHREAD);
84 }
85
86 ~watchdog()
87 {
88 // INVALID_HANDLE_VALUE waits for a callback already running, so a case finishing on the
89 // deadline cannot race its own teardown.
90 if(timer_) DeleteTimerQueueTimer(nullptr, timer_, INVALID_HANDLE_VALUE);
91 }
92
93 watchdog(watchdog const&) = delete;
94 watchdog& operator=(watchdog const&) = delete;
95
96 HANDLE timer_ = nullptr;
97 };
98 }
99 #else
100 extern "C"
101 {
102 ✗ [[noreturn]] inline void tts_watchdog_expired(int)
103 {
104 static bool reporting = false;
105 ✗ if(reporting) ::tts::_::exit_now();
106 ✗ reporting = true;
107
108 ✗ ::tts::_::report_timeout();
109 ✗ ::tts::_::perform_abort(0);
110 }
111 }
112
113 namespace tts::_
114 {
115 struct watchdog
116 {
117 282 explicit watchdog(nanoseconds ns)
118 282 {
119
1/2
✓ Branch 0 taken 282 times.
✗ Branch 1 not taken.
282 if(!ns) return;
120
121 ✗ watchdog_ns = ns;
122
123 ✗ struct sigaction action = {};
124 ✗ action.sa_handler = &tts_watchdog_expired;
125 ✗ action.sa_flags = 0;
126 ✗ sigemptyset(&action.sa_mask);
127 ✗ sigaction(SIGALRM, &action, &previous_);
128
129 // A thread blocked in a syscall still takes the signal, which a polling thread would not.
130 ✗ itimerval deadline {};
131 ✗ deadline.it_value.tv_sec = static_cast<time_t>(ns / 1'000'000'000u);
132 ✗ deadline.it_value.tv_usec = static_cast<suseconds_t>((ns % 1'000'000'000u) / 1000u);
133 ✗ setitimer(ITIMER_REAL, &deadline, nullptr);
134
135 ✗ armed_ = true;
136 }
137
138 282 ~watchdog()
139 ✗ {
140
1/2
✓ Branch 0 taken 282 times.
✗ Branch 1 not taken.
282 if(!armed_) return;
141
142 ✗ itimerval off {};
143 ✗ setitimer(ITIMER_REAL, &off, nullptr);
144 ✗ sigaction(SIGALRM, &previous_, nullptr);
145 282 }
146
147 watchdog(watchdog const&) = delete;
148 watchdog& operator=(watchdog const&) = delete;
149
150 private:
151 struct sigaction previous_ = {};
152 bool armed_ = false;
153 };
154 }
155 #endif
156