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

88.0% Lines (44/50) 100.0% List of functions (7/7) 77.4% Branches (24/31)
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, 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 369x inline win_timers_thread::win_timers_thread(
48 369x void* iocp, long* dispatch_required) noexcept
49 : win_timers(dispatch_required)
50 369x , iocp_(iocp)
51 {
52 369x waitable_timer_ = ::CreateWaitableTimerW(nullptr, FALSE, nullptr);
53 369x }
54
55 738x inline win_timers_thread::~win_timers_thread()
56 {
57 369x stop();
58
1/2
✓ Branch 3 → 4 taken 369 times.
✗ Branch 3 → 5 not taken.
369x if (waitable_timer_)
59 369x ::CloseHandle(waitable_timer_);
60 738x }
61
62 inline void
63 369x win_timers_thread::start()
64 {
65
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 369 times.
369x if (!waitable_timer_)
66 return;
67
68
1/1
✓ Branch 4 → 5 taken 369 times.
738x thread_ = std::thread([this] { thread_func(); });
69 }
70
71 inline void
72 738x win_timers_thread::stop()
73 {
74
2/2
✓ Branch 4 → 5 taken 369 times.
✓ Branch 4 → 8 taken 369 times.
1476x if (::InterlockedExchange(&shutdown_, 1) == 0)
75 {
76 // Wake the timer thread by setting timer to fire immediately
77
1/2
✓ Branch 5 → 6 taken 369 times.
✗ Branch 5 → 8 not taken.
369x if (waitable_timer_)
78 {
79 LARGE_INTEGER due_time;
80 369x due_time.QuadPart = 0;
81
1/1
✓ Branch 6 → 7 taken 369 times.
369x ::SetWaitableTimer(
82 waitable_timer_, &due_time, 0, nullptr, nullptr, FALSE);
83 }
84 }
85
86
2/2
✓ Branch 9 → 10 taken 369 times.
✓ Branch 9 → 11 taken 369 times.
738x if (thread_.joinable())
87 {
88 try
89 {
90
1/1
✓ Branch 10 → 11 taken 369 times.
369x 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 738x }
100
101 inline void
102 3088x win_timers_thread::update_timeout(time_point next_expiry)
103 {
104
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3088 times.
3088x if (!waitable_timer_)
105 return;
106
107 3088x auto now = std::chrono::steady_clock::now();
108 LARGE_INTEGER due_time;
109
110
3/3
✓ Branch 5 → 6 taken 3088 times.
✓ Branch 7 → 8 taken 876 times.
✓ Branch 7 → 9 taken 2212 times.
3088x if (next_expiry <= now)
111 {
112 // Already expired - fire immediately
113 876x due_time.QuadPart = 0;
114 }
115
3/3
✓ Branch 10 → 11 taken 2212 times.
✓ Branch 11 → 12 taken 59 times.
✓ Branch 11 → 13 taken 2153 times.
2212x else if (next_expiry == (time_point::max)())
116 {
117 // No timers - set far future (max 49 days in 100ns units)
118 59x 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 2153 times.
2153x auto duration = next_expiry - now;
124 2153x auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration)
125 2153x .count();
126 2153x due_time.QuadPart = -(ns / 100);
127
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 2153 times.
2153x if (due_time.QuadPart == 0)
128 due_time.QuadPart = -1; // At least 100ns
129 }
130
131
1/1
✓ Branch 19 → 20 taken 3088 times.
3088x ::SetWaitableTimer(waitable_timer_, &due_time, 0, nullptr, nullptr, FALSE);
132 }
133
134 inline void
135 369x win_timers_thread::thread_func()
136 {
137
1/2
✓ Branch 15 → 3 taken 1530 times.
✗ Branch 15 → 16 not taken.
3060x while (::InterlockedExchangeAdd(&shutdown_, 0) == 0)
138 {
139 1530x DWORD result = ::WaitForSingleObject(waitable_timer_, INFINITE);
140
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1530 times.
1530x if (result != WAIT_OBJECT_0)
141 break;
142
143
2/2
✓ Branch 8 → 9 taken 369 times.
✓ Branch 8 → 10 taken 1161 times.
3060x if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
144 369x break;
145
146 1161x ::InterlockedExchange(dispatch_required_, 1);
147 1161x ::PostQueuedCompletionStatus(
148 1161x static_cast<HANDLE>(iocp_), 0, key_wake_dispatch, nullptr);
149 }
150 369x }
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