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

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