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

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