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

94.0% Lines (47/50) 100.0% List of functions (7/7) 96.8% Branches (30/31)
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 <thread>
22
23 namespace boost::corosio::detail {
24
25 class win_timers_thread final : public win_timers
26 {
27 void* iocp_;
28 void* waitable_timer_ = nullptr;
29 std::thread thread_;
30 long shutdown_ = 0;
31
32 public:
33 win_timers_thread(void* iocp_handle, long* dispatch_required) noexcept;
34 ~win_timers_thread();
35
36 win_timers_thread(win_timers_thread const&) = delete;
37 win_timers_thread& operator=(win_timers_thread const&) = delete;
38
39 void start() override;
40 void stop() override;
41 void update_timeout(time_point next_expiry) override;
42
43 private:
44 void thread_func();
45 };
46
47 4581x inline win_timers_thread::win_timers_thread(
48 4581x void* iocp_handle, long* dispatch_required) noexcept
49 : win_timers(dispatch_required)
50 4581x , iocp_(iocp_handle)
51 {
52 4581x waitable_timer_ = ::CreateWaitableTimerW(nullptr, FALSE, nullptr);
53 4581x }
54
55 9162x inline win_timers_thread::~win_timers_thread()
56 {
57 4581x stop();
58
2/2
✓ Branch 3 → 4 taken 4580 times.
✓ Branch 3 → 5 taken 1 time.
4581x if (waitable_timer_)
59 4580x ::CloseHandle(waitable_timer_);
60 9162x }
61
62 inline void
63 4581x win_timers_thread::start()
64 {
65
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 4580 times.
4581x if (!waitable_timer_)
66 1x return;
67
68
1/1
✓ Branch 4 → 5 taken 4580 times.
9160x thread_ = std::thread([this] { thread_func(); });
69 }
70
71 inline void
72 9009x win_timers_thread::stop()
73 {
74
2/2
✓ Branch 4 → 5 taken 4581 times.
✓ Branch 4 → 8 taken 4428 times.
18018x if (::InterlockedExchange(&shutdown_, 1) == 0)
75 {
76 // Wake the timer thread by setting timer to fire immediately
77
2/2
✓ Branch 5 → 6 taken 4580 times.
✓ Branch 5 → 8 taken 1 time.
4581x if (waitable_timer_)
78 {
79 LARGE_INTEGER due_time;
80 4580x due_time.QuadPart = 0;
81
1/1
✓ Branch 6 → 7 taken 4580 times.
4580x ::SetWaitableTimer(
82 waitable_timer_, &due_time, 0, nullptr, nullptr, FALSE);
83 }
84 }
85
86
2/2
✓ Branch 9 → 10 taken 4580 times.
✓ Branch 9 → 11 taken 4429 times.
9009x if (thread_.joinable())
87 {
88 try
89 {
90
1/1
✓ Branch 10 → 11 taken 4580 times.
4580x thread_.join();
91 }
92 ✗ catch (...)
93 {
94 // Swallow join failures — called from destructors and
95 // noexcept shutdown paths. The thread has been signalled
96 // to exit; if join fails the OS will reclaim it.
97 ✗ }
98 }
99 9009x }
100
101 inline void
102 3355x win_timers_thread::update_timeout(time_point next_expiry)
103 {
104
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 3354 times.
3355x if (!waitable_timer_)
105 1x return;
106
107 3354x auto now = std::chrono::steady_clock::now();
108 LARGE_INTEGER due_time;
109
110
3/3
✓ Branch 5 → 6 taken 3354 times.
✓ Branch 7 → 8 taken 548 times.
✓ Branch 7 → 9 taken 2806 times.
3354x if (next_expiry <= now)
111 {
112 // Already expired - fire immediately
113 548x due_time.QuadPart = 0;
114 }
115
3/3
✓ Branch 10 → 11 taken 2806 times.
✓ Branch 11 → 12 taken 150 times.
✓ Branch 11 → 13 taken 2656 times.
2806x else if (next_expiry == (time_point::max)())
116 {
117 // No timers - set far future (max 49 days in 100ns units)
118 150x due_time.QuadPart = -LONGLONG(49) * 24 * 60 * 60 * 10000000LL;
119 }
120 else
121 {
122 // Convert duration to 100ns units (negative = relative)
123
1/1
✓ Branch 13 → 14 taken 2656 times.
2656x auto duration = next_expiry - now;
124 2656x auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration)
125 2656x .count();
126 2656x due_time.QuadPart = -(ns / 100);
127
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 2656 times.
2656x if (due_time.QuadPart == 0)
128 ✗ due_time.QuadPart = -1; // At least 100ns
129 }
130
131
1/1
✓ Branch 19 → 20 taken 3354 times.
3354x ::SetWaitableTimer(waitable_timer_, &due_time, 0, nullptr, nullptr, FALSE);
132 }
133
134 inline void
135 4580x win_timers_thread::thread_func()
136 {
137
2/2
✓ Branch 15 → 3 taken 4979 times.
✓ Branch 15 → 16 taken 493 times.
10944x while (::InterlockedExchangeAdd(&shutdown_, 0) == 0)
138 {
139 4979x DWORD result = ::WaitForSingleObject(waitable_timer_, INFINITE);
140
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 4978 times.
4979x if (result != WAIT_OBJECT_0)
141 1x break;
142
143
2/2
✓ Branch 8 → 9 taken 4086 times.
✓ Branch 8 → 10 taken 892 times.
9956x if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
144 4086x break;
145
146 892x ::InterlockedExchange(dispatch_required_, 1);
147 892x ::PostQueuedCompletionStatus(
148 892x static_cast<HANDLE>(iocp_), 0, key_wake_dispatch, nullptr);
149 }
150 4580x }
151
152 } // namespace boost::corosio::detail
153
154 #endif // BOOST_COROSIO_HAS_IOCP
155
156 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TIMERS_THREAD_HPP
157