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

95.8% Lines (23/24) 100.0% List of functions (6/6) 76.9% Branches (10/13)
win_wsa_init.hpp
f(x) Functions (6)
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_WSA_INIT_HPP
12 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_WSA_INIT_HPP
13
14 #include <boost/corosio/detail/platform.hpp>
15
16 #if BOOST_COROSIO_HAS_IOCP
17
18 #include <boost/corosio/detail/config.hpp>
19 #include <boost/corosio/native/detail/make_err.hpp>
20 #include <boost/corosio/detail/except.hpp>
21
22 #include <boost/corosio/native/detail/iocp/win_windows.hpp>
23
24 namespace boost::corosio::detail {
25
26 /** RAII class for Winsock initialization.
27
28 Uses reference counting to ensure WSAStartup is called once on
29 first construction and WSACleanup on last destruction.
30
31 Derive from this class to ensure Winsock is initialized before
32 any socket operations.
33 */
34 class BOOST_COROSIO_DECL win_wsa_init
35 {
36 protected:
37 win_wsa_init();
38 ~win_wsa_init();
39
40 win_wsa_init(win_wsa_init const&) = delete;
41 win_wsa_init& operator=(win_wsa_init const&) = delete;
42 };
43
44 // Process-wide Winsock init refcount. Kept as an inline function-local
45 // static (one shared instance across TUs) rather than a static data member
46 // so win_wsa_init can carry BOOST_COROSIO_DECL: an exported/imported static
47 // data member defined in a header is rejected by MSVC and clang-cl
48 // ("definition of dllimport static field not allowed").
49 inline long&
50 11434x win_wsa_init_count() noexcept
51 {
52 static long count = 0;
53 11434x return count;
54 }
55
56 // Hold Winsock for the life of the process, so the count never falls back
57 // to zero between one io_context and the next. An asynchronous
58 // GetAddrInfoExW keeps settling inside ws2_32 past its completion
59 // routine, with no way to observe when, and a WSACleanup under it faults
60 // on a system DNS thread.
61 inline void
62 5611x win_wsa_hold_process() noexcept
63 {
64 struct holder
65 {
66 89x holder() noexcept
67 {
68 89x ::InterlockedIncrement(&win_wsa_init_count());
69 89x }
70
71 89x ~holder()
72 {
73
1/2
✓ Branch 5 → 6 taken 89 times.
✗ Branch 5 → 7 not taken.
178x if (::InterlockedDecrement(&win_wsa_init_count()) == 0)
74 89x ::WSACleanup();
75 89x }
76 };
77
78
3/4
✓ Branch 2 → 3 taken 89 times.
✓ Branch 2 → 8 taken 5522 times.
✓ Branch 4 → 5 taken 89 times.
✗ Branch 4 → 8 not taken.
5611x static holder const instance;
79 5611x }
80
81 5628x inline win_wsa_init::win_wsa_init()
82 {
83
2/2
✓ Branch 5 → 6 taken 106 times.
✓ Branch 5 → 14 taken 5522 times.
11256x if (::InterlockedIncrement(&win_wsa_init_count()) == 1)
84 {
85 WSADATA wsaData;
86
1/1
✓ Branch 6 → 7 taken 106 times.
106x int result = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
87
2/2
✓ Branch 7 → 8 taken 17 times.
✓ Branch 7 → 13 taken 89 times.
106x if (result != 0)
88 {
89 17x ::InterlockedDecrement(&win_wsa_init_count());
90 17x throw_system_error(make_err(result));
91 }
92 }
93
94 // After the startup above, which must own the first count.
95 5611x win_wsa_hold_process();
96 5611x }
97
98 5611x inline win_wsa_init::~win_wsa_init()
99 {
100
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 5611 times.
11222x if (::InterlockedDecrement(&win_wsa_init_count()) == 0)
101 ✗ ::WSACleanup();
102 5611x }
103
104 } // namespace boost::corosio::detail
105
106 #endif // BOOST_COROSIO_HAS_IOCP
107
108 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_WSA_INIT_HPP
109