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

91.7% Lines (44/48) 100.0% Functions (7/7) 80.0% Branches (24/30)
include/boost/corosio/native/detail/iocp/win_timers_thread.hpp
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 290 inline win_timers_thread::win_timers_thread(
48 290 void* iocp, long* dispatch_required) noexcept
49 : win_timers(dispatch_required)
50 290 , iocp_(iocp)
51 {
52 290 waitable_timer_ = ::CreateWaitableTimerW(nullptr, FALSE, nullptr);
53 290 }
54
55 580 inline win_timers_thread::~win_timers_thread()
56 {
57 290 stop();
58
1/2
✓ Branch 3 → 4 taken 290 times.
✗ Branch 3 → 5 not taken.
290 if (waitable_timer_)
59 290 ::CloseHandle(waitable_timer_);
60 580 }
61
62 inline void
63 290 win_timers_thread::start()
64 {
65
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 290 times.
290 if (!waitable_timer_)
66 return;
67
68
1/1
✓ Branch 4 → 5 taken 290 times.
580 thread_ = std::thread([this] { thread_func(); });
69 }
70
71 inline void
72 580 win_timers_thread::stop()
73 {
74
2/2
✓ Branch 4 → 5 taken 290 times.
✓ Branch 4 → 8 taken 290 times.
1160 if (::InterlockedExchange(&shutdown_, 1) == 0)
75 {
76 // Wake the timer thread by setting timer to fire immediately
77
1/2
✓ Branch 5 → 6 taken 290 times.
✗ Branch 5 → 8 not taken.
290 if (waitable_timer_)
78 {
79 LARGE_INTEGER due_time;
80 290 due_time.QuadPart = 0;
81
1/1
✓ Branch 6 → 7 taken 290 times.
290 ::SetWaitableTimer(
82 waitable_timer_, &due_time, 0, nullptr, nullptr, FALSE);
83 }
84 }
85
86
2/2
✓ Branch 9 → 10 taken 290 times.
✓ Branch 9 → 11 taken 290 times.
580 if (thread_.joinable())
87 290 thread_.join();
88 580 }
89
90 inline void
91 2800 win_timers_thread::update_timeout(time_point next_expiry)
92 {
93
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2800 times.
2800 if (!waitable_timer_)
94 return;
95
96 2800 auto now = std::chrono::steady_clock::now();
97 LARGE_INTEGER due_time;
98
99
3/3
✓ Branch 5 → 6 taken 2800 times.
✓ Branch 7 → 8 taken 782 times.
✓ Branch 7 → 9 taken 2018 times.
2800 if (next_expiry <= now)
100 {
101 // Already expired - fire immediately
102 782 due_time.QuadPart = 0;
103 }
104
3/3
✓ Branch 10 → 11 taken 2018 times.
✓ Branch 11 → 12 taken 46 times.
✓ Branch 11 → 13 taken 1972 times.
2018 else if (next_expiry == (time_point::max)())
105 {
106 // No timers - set far future (max 49 days in 100ns units)
107 46 due_time.QuadPart = -LONGLONG(49) * 24 * 60 * 60 * 10000000LL;
108 }
109 else
110 {
111 // Convert duration to 100ns units (negative = relative)
112
1/1
✓ Branch 13 → 14 taken 1972 times.
1972 auto duration = next_expiry - now;
113 1972 auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration)
114 1972 .count();
115 1972 due_time.QuadPart = -(ns / 100);
116
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 1972 times.
1972 if (due_time.QuadPart == 0)
117 due_time.QuadPart = -1; // At least 100ns
118 }
119
120
1/1
✓ Branch 19 → 20 taken 2800 times.
2800 ::SetWaitableTimer(waitable_timer_, &due_time, 0, nullptr, nullptr, FALSE);
121 }
122
123 inline void
124 290 win_timers_thread::thread_func()
125 {
126
2/2
✓ Branch 15 → 3 taken 1300 times.
✓ Branch 15 → 16 taken 26 times.
2652 while (::InterlockedExchangeAdd(&shutdown_, 0) == 0)
127 {
128 1300 DWORD result = ::WaitForSingleObject(waitable_timer_, INFINITE);
129
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1300 times.
1300 if (result != WAIT_OBJECT_0)
130 break;
131
132
2/2
✓ Branch 8 → 9 taken 264 times.
✓ Branch 8 → 10 taken 1036 times.
2600 if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
133 264 break;
134
135 1036 ::InterlockedExchange(dispatch_required_, 1);
136 1036 ::PostQueuedCompletionStatus(
137 1036 static_cast<HANDLE>(iocp_), 0, key_wake_dispatch, nullptr);
138 }
139 290 }
140
141 } // namespace boost::corosio::detail
142
143 #endif // BOOST_COROSIO_HAS_IOCP
144
145 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_TIMERS_THREAD_HPP
146