include/boost/corosio/native/detail/iocp/win_timers_thread.hpp

83.9% Lines (47 / 56) 85.3% Branches (29 / 34) 100.0% Functions (7 / 7)
win_timers_thread.hpp
f(x) Functions (7)
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Steve Gerbino
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TIMERS_THREAD_HPP
12 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TIMERS_THREAD_HPP
13
14 #include <boost/corosio/detail/platform.hpp>
15
16 #if BOOST_COROSIO_HAS_IOCP
17
18 #include <boost/corosio/native/detail/iocp/win_timers.hpp>
19 #include <boost/corosio/native/detail/iocp/win_completion_key.hpp>
20 #include <boost/corosio/native/detail/iocp/win_windows.hpp>
21 #include <system_error>
22 #include <thread>
23
24 namespace boost::corosio::detail {
25
26 class win_timers_thread final : public win_timers
27 {
28 void* iocp_;
29 void* waitable_timer_ = nullptr;
30 std::thread thread_;
31 long shutdown_ = 0;
32
33 public:
34 win_timers_thread(void* iocp_handle, long* dispatch_required) noexcept;
35 ~win_timers_thread();
36
37 win_timers_thread(win_timers_thread const&) = delete;
38 win_timers_thread& operator=(win_timers_thread const&) = delete;
39
40 bool start() override;
41 void stop() override;
42 void update_timeout(time_point next_expiry) override;
43
44 private:
45 void thread_func();
46 };
47
48 420x inline win_timers_thread::win_timers_thread(
49 420x void* iocp_handle, long* dispatch_required) noexcept
50 : win_timers(dispatch_required)
51 420x , iocp_(iocp_handle)
52 {
53 420x }
54
55 840x inline win_timers_thread::~win_timers_thread()
56 {
57 420x stop();
58
2/2
✓ Branch 3 → 4 taken 403 times.
✓ Branch 3 → 5 taken 17 times.
420x if (waitable_timer_)
59 403x ::CloseHandle(waitable_timer_);
60 840x }
61
62 inline bool
63 420x win_timers_thread::start()
64 {
65 420x waitable_timer_ = ::CreateWaitableTimerW(nullptr, FALSE, nullptr);
66
2/2
✓ Branch 3 → 4 taken 17 times.
✓ Branch 3 → 5 taken 403 times.
420x if (!waitable_timer_)
67 17x return false;
68 try
69 {
70
1/1
✓ Branch 5 → 6 taken 403 times.
806x thread_ = std::thread([this] { thread_func(); });
71 }
72
0/2
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 14 not taken.
✗ catch (std::system_error const&)
73 {
74
0/1
✗ Branch 15 → 16 not taken.
✗ ::CloseHandle(waitable_timer_);
75 ✗ waitable_timer_ = nullptr;
76 ✗ return false;
77 ✗ }
78 403x return true;
79 }
80
81 inline void
82 823x win_timers_thread::stop()
83 {
84
2/2
✓ Branch 4 → 5 taken 420 times.
✓ Branch 4 → 8 taken 403 times.
1646x if (::InterlockedExchange(&shutdown_, 1) == 0)
85 {
86 // Wake the timer thread by setting timer to fire immediately
87
2/2
✓ Branch 5 → 6 taken 403 times.
✓ Branch 5 → 8 taken 17 times.
420x if (waitable_timer_)
88 {
89 LARGE_INTEGER due_time;
90 403x due_time.QuadPart = 0;
91
1/1
✓ Branch 6 → 7 taken 403 times.
403x ::SetWaitableTimer(
92 waitable_timer_, &due_time, 0, nullptr, nullptr, FALSE);
93 }
94 }
95
96
2/2
✓ Branch 9 → 10 taken 403 times.
✓ Branch 9 → 11 taken 420 times.
823x if (thread_.joinable())
97 {
98 try
99 {
100
1/1
✓ Branch 10 → 11 taken 403 times.
403x thread_.join();
101 }
102 ✗ catch (...)
103 {
104 // Swallow join failures — called from destructors and
105 // noexcept shutdown paths. The thread has been signalled
106 // to exit; if join fails the OS will reclaim it.
107 ✗ }
108 }
109 823x }
110
111 inline void
112 2673x win_timers_thread::update_timeout(time_point next_expiry)
113 {
114
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2673 times.
2673x if (!waitable_timer_)
115 ✗ return;
116
117 2673x auto now = std::chrono::steady_clock::now();
118 LARGE_INTEGER due_time;
119
120
3/3
✓ Branch 5 → 6 taken 2673 times.
✓ Branch 7 → 8 taken 61 times.
✓ Branch 7 → 9 taken 2612 times.
2673x if (next_expiry <= now)
121 {
122 // Already expired - fire immediately
123 61x due_time.QuadPart = 0;
124 }
125
3/3
✓ Branch 10 → 11 taken 2612 times.
✓ Branch 11 → 12 taken 148 times.
✓ Branch 11 → 13 taken 2464 times.
2612x else if (next_expiry == (time_point::max)())
126 {
127 // No timers - set far future (max 49 days in 100ns units)
128 148x due_time.QuadPart = -LONGLONG(49) * 24 * 60 * 60 * 10000000LL;
129 }
130 else
131 {
132 // Convert duration to 100ns units (negative = relative)
133
1/1
✓ Branch 13 → 14 taken 2464 times.
2464x auto duration = next_expiry - now;
134 2464x auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration)
135 2464x .count();
136 2464x due_time.QuadPart = -(ns / 100);
137
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 2464 times.
2464x if (due_time.QuadPart == 0)
138 ✗ due_time.QuadPart = -1; // At least 100ns
139 }
140
141
1/1
✓ Branch 19 → 20 taken 2673 times.
2673x ::SetWaitableTimer(waitable_timer_, &due_time, 0, nullptr, nullptr, FALSE);
142 }
143
144 inline void
145 403x win_timers_thread::thread_func()
146 {
147
2/2
✓ Branch 15 → 3 taken 669 times.
✓ Branch 15 → 16 taken 138 times.
1614x while (::InterlockedExchangeAdd(&shutdown_, 0) == 0)
148 {
149 669x DWORD result = ::WaitForSingleObject(waitable_timer_, INFINITE);
150
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 668 times.
669x if (result != WAIT_OBJECT_0)
151 1x break;
152
153
2/2
✓ Branch 8 → 9 taken 264 times.
✓ Branch 8 → 10 taken 404 times.
1336x if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
154 264x break;
155
156 404x ::InterlockedExchange(dispatch_required_, 1);
157 404x ::PostQueuedCompletionStatus(
158 404x static_cast<HANDLE>(iocp_), 0, key_wake_dispatch, nullptr);
159 }
160 403x }
161
162 } // namespace boost::corosio::detail
163
164 #endif // BOOST_COROSIO_HAS_IOCP
165
166 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TIMERS_THREAD_HPP
167