1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_DETAIL_THREAD_LOCAL_PTR_HPP
10  
#ifndef BOOST_COROSIO_DETAIL_THREAD_LOCAL_PTR_HPP
11  
#define BOOST_COROSIO_DETAIL_THREAD_LOCAL_PTR_HPP
11  
#define BOOST_COROSIO_DETAIL_THREAD_LOCAL_PTR_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  

14  

15  
#include <type_traits>
15  
#include <type_traits>
16  

16  

17  
// Detect thread-local storage mechanism
17  
// Detect thread-local storage mechanism
18  
#if !defined(BOOST_COROSIO_TLS_KEYWORD)
18  
#if !defined(BOOST_COROSIO_TLS_KEYWORD)
19 -
# if defined(_MSC_VER)
19 +
#if defined(_MSC_VER)
20 -
#  define BOOST_COROSIO_TLS_KEYWORD __declspec(thread)
20 +
#define BOOST_COROSIO_TLS_KEYWORD __declspec(thread)
21 -
# elif defined(__GNUC__) || defined(__clang__)
21 +
#elif defined(__GNUC__) || defined(__clang__)
22 -
#  define BOOST_COROSIO_TLS_KEYWORD __thread
22 +
#define BOOST_COROSIO_TLS_KEYWORD __thread
23 -
# endif
23 +
#endif
24  
#endif
24  
#endif
25  

25  

26  
namespace boost::corosio::detail {
26  
namespace boost::corosio::detail {
27  

27  

28  
/** A thread-local pointer.
28  
/** A thread-local pointer.
29  

29  

30  
    This class provides thread-local storage for a pointer to T.
30  
    This class provides thread-local storage for a pointer to T.
31  
    Each thread has its own independent pointer value, initially
31  
    Each thread has its own independent pointer value, initially
32  
    nullptr. The user is responsible for managing the lifetime
32  
    nullptr. The user is responsible for managing the lifetime
33  
    of the pointed-to objects.
33  
    of the pointed-to objects.
34  

34  

35  
    The storage is static per type T. All instances of
35  
    The storage is static per type T. All instances of
36  
    `thread_local_ptr<T>` share the same underlying slot.
36  
    `thread_local_ptr<T>` share the same underlying slot.
37  

37  

38  
    The implementation uses the most efficient available mechanism:
38  
    The implementation uses the most efficient available mechanism:
39  
    1. Compiler keyword (__declspec(thread) or __thread) - enforces POD
39  
    1. Compiler keyword (__declspec(thread) or __thread) - enforces POD
40  
    2. C++11 thread_local (fallback)
40  
    2. C++11 thread_local (fallback)
41  

41  

42  
    @tparam T The pointed-to type.
42  
    @tparam T The pointed-to type.
43  

43  

44  
    @par Declaration
44  
    @par Declaration
45  

45  

46  
    Typically declared at namespace or class scope. The object
46  
    Typically declared at namespace or class scope. The object
47  
    is stateless, so local variables work but are redundant.
47  
    is stateless, so local variables work but are redundant.
48  

48  

49  
    @code
49  
    @code
50  
    // Recommended: namespace scope
50  
    // Recommended: namespace scope
51  
    namespace {
51  
    namespace {
52  
    thread_local_ptr<session> current_session;
52  
    thread_local_ptr<session> current_session;
53  
    }
53  
    }
54  

54  

55  
    // Also works: static class member
55  
    // Also works: static class member
56  
    class server {
56  
    class server {
57  
        static thread_local_ptr<request> current_request_;
57  
        static thread_local_ptr<request> current_request_;
58  
    };
58  
    };
59  

59  

60  
    // Works but unusual: local variable (still accesses static storage)
60  
    // Works but unusual: local variable (still accesses static storage)
61  
    void foo() {
61  
    void foo() {
62  
        thread_local_ptr<context> ctx;  // same slot on every call
62  
        thread_local_ptr<context> ctx;  // same slot on every call
63  
        ctx = new context();
63  
        ctx = new context();
64  
    }
64  
    }
65  
    @endcode
65  
    @endcode
66  

66  

67  
    @note The user is responsible for deleting pointed-to objects
67  
    @note The user is responsible for deleting pointed-to objects
68  
    before threads exit to avoid memory leaks.
68  
    before threads exit to avoid memory leaks.
69  
*/
69  
*/
70  
template<class T>
70  
template<class T>
71  
class thread_local_ptr;
71  
class thread_local_ptr;
72 -
//------------------------------------------------------------------------------
 
73  

72  

74  

73  

75  
#if defined(BOOST_COROSIO_TLS_KEYWORD)
74  
#if defined(BOOST_COROSIO_TLS_KEYWORD)
76  

75  

77  
// Use compiler-specific keyword (__declspec(thread) or __thread)
76  
// Use compiler-specific keyword (__declspec(thread) or __thread)
78  
// Most efficient: static linkage, no dynamic init, enforces POD
77  
// Most efficient: static linkage, no dynamic init, enforces POD
79  

78  

80  
template<class T>
79  
template<class T>
81  
class thread_local_ptr
80  
class thread_local_ptr
82  
{
81  
{
83  
    static BOOST_COROSIO_TLS_KEYWORD T* ptr_;
82  
    static BOOST_COROSIO_TLS_KEYWORD T* ptr_;
84  

83  

85  
public:
84  
public:
86  
    thread_local_ptr() = default;
85  
    thread_local_ptr() = default;
87  
    ~thread_local_ptr() = default;
86  
    ~thread_local_ptr() = default;
88  

87  

89  
    thread_local_ptr(thread_local_ptr const&) = delete;
88  
    thread_local_ptr(thread_local_ptr const&) = delete;
90  
    thread_local_ptr& operator=(thread_local_ptr const&) = delete;
89  
    thread_local_ptr& operator=(thread_local_ptr const&) = delete;
91  

90  

92  
    /** Return the pointer for this thread.
91  
    /** Return the pointer for this thread.
93  

92  

94  
        @return The stored pointer, or nullptr if not set.
93  
        @return The stored pointer, or nullptr if not set.
95  
    */
94  
    */
96 -
    T*
95 +
    T* get() const noexcept
97 -
    get() const noexcept
 
98  
    {
96  
    {
99  
        return ptr_;
97  
        return ptr_;
100  
    }
98  
    }
101  

99  

102  
    /** Set the pointer for this thread.
100  
    /** Set the pointer for this thread.
103  

101  

104  
        @param p The pointer to store. The user manages its lifetime.
102  
        @param p The pointer to store. The user manages its lifetime.
105  
    */
103  
    */
106 -
    void
104 +
    void set(T* p) noexcept
107 -
    set(T* p) noexcept
 
108  
    {
105  
    {
109  
        ptr_ = p;
106  
        ptr_ = p;
110  
    }
107  
    }
111  

108  

112  
    /** Dereference the stored pointer.
109  
    /** Dereference the stored pointer.
113  

110  

114  
        @pre get() != nullptr
111  
        @pre get() != nullptr
115  
    */
112  
    */
116 -
    T&
113 +
    T& operator*() const noexcept
117 -
    operator*() const noexcept
 
118  
    {
114  
    {
119  
        return *ptr_;
115  
        return *ptr_;
120  
    }
116  
    }
121  

117  

122  
    /** Member access through the stored pointer.
118  
    /** Member access through the stored pointer.
123  

119  

124  
        @pre get() != nullptr
120  
        @pre get() != nullptr
125  
    */
121  
    */
126 -
    T*
122 +
    T* operator->() const noexcept
127 -
    operator->() const noexcept
 
128  
        requires std::is_class_v<T>
123  
        requires std::is_class_v<T>
129  
    {
124  
    {
130  
        return ptr_;
125  
        return ptr_;
131  
    }
126  
    }
132  

127  

133  
    /** Assign a pointer value.
128  
    /** Assign a pointer value.
134  

129  

135  
        @param p The pointer to store.
130  
        @param p The pointer to store.
136  
        @return The stored pointer.
131  
        @return The stored pointer.
137  
    */
132  
    */
138 -
    T*
133 +
    // NOLINTNEXTLINE(misc-unconventional-assign-operator)
139 -
    operator=(T* p) noexcept
134 +
    T* operator=(T* p) noexcept
140  
    {
135  
    {
141  
        ptr_ = p;
136  
        ptr_ = p;
142  
        return p;
137  
        return p;
143  
    }
138  
    }
144  
};
139  
};
145  

140  

146  
template<class T>
141  
template<class T>
147  
BOOST_COROSIO_TLS_KEYWORD T* thread_local_ptr<T>::ptr_ = nullptr;
142  
BOOST_COROSIO_TLS_KEYWORD T* thread_local_ptr<T>::ptr_ = nullptr;
148 -
//------------------------------------------------------------------------------
 
149  

143  

150  

144  

151  
#else
145  
#else
152  

146  

153  
// Use C++11 thread_local keyword (fallback)
147  
// Use C++11 thread_local keyword (fallback)
154  

148  

155  
template<class T>
149  
template<class T>
156  
class thread_local_ptr
150  
class thread_local_ptr
157  
{
151  
{
158  
    static thread_local T* ptr_;
152  
    static thread_local T* ptr_;
159  

153  

160  
public:
154  
public:
161  
    thread_local_ptr() = default;
155  
    thread_local_ptr() = default;
162  
    ~thread_local_ptr() = default;
156  
    ~thread_local_ptr() = default;
163  

157  

164  
    thread_local_ptr(thread_local_ptr const&) = delete;
158  
    thread_local_ptr(thread_local_ptr const&) = delete;
165  
    thread_local_ptr& operator=(thread_local_ptr const&) = delete;
159  
    thread_local_ptr& operator=(thread_local_ptr const&) = delete;
166  

160  

167 -
    T*
161 +
    T* get() const noexcept
168 -
    get() const noexcept
 
169  
    {
162  
    {
170  
        return ptr_;
163  
        return ptr_;
171  
    }
164  
    }
172  

165  

173 -
    void
166 +
    void set(T* p) noexcept
174 -
    set(T* p) noexcept
 
175  
    {
167  
    {
176  
        ptr_ = p;
168  
        ptr_ = p;
177  
    }
169  
    }
178  

170  

179 -
    T&
171 +
    T& operator*() const noexcept
180 -
    operator*() const noexcept
 
181  
    {
172  
    {
182  
        return *ptr_;
173  
        return *ptr_;
183  
    }
174  
    }
184  

175  

185 -
    T*
176 +
    T* operator->() const noexcept
186 -
    operator->() const noexcept
 
187  
        requires std::is_class_v<T>
177  
        requires std::is_class_v<T>
188  
    {
178  
    {
189  
        return ptr_;
179  
        return ptr_;
190  
    }
180  
    }
191  

181  

192 -
    T*
182 +
    // NOLINTNEXTLINE(misc-unconventional-assign-operator)
193 -
    operator=(T* p) noexcept
183 +
    T* operator=(T* p) noexcept
194  
    {
184  
    {
195  
        ptr_ = p;
185  
        ptr_ = p;
196  
        return p;
186  
        return p;
197  
    }
187  
    }
198  
};
188  
};
199  

189  

200  
template<class T>
190  
template<class T>
201  
thread_local T* thread_local_ptr<T>::ptr_ = nullptr;
191  
thread_local T* thread_local_ptr<T>::ptr_ = nullptr;
202  

192  

203  
#endif
193  
#endif
204  

194  

205  
} // namespace boost::corosio::detail
195  
} // namespace boost::corosio::detail
206  

196  

207  
#endif
197  
#endif