Enable proper full C++ exception handling on MSVC
[openal-soft.git] / common / althreads.h
blobd8491706713004e1d4ef1e6bb23b3a5cad89a9c2
1 #ifndef AL_THREADS_H
2 #define AL_THREADS_H
4 #include <cstdint>
5 #include <stdexcept>
6 #include <type_traits>
8 #ifdef _WIN32
9 #define WIN32_LEAN_AND_MEAN
10 #include <windows.h>
12 #elif defined(__APPLE__)
14 #include <pthread.h>
16 #else
18 #include <threads.h>
19 #endif
21 #include "albit.h"
23 namespace al {
25 template<typename T>
26 class tss {
27 static_assert(sizeof(T) <= sizeof(void*));
28 static_assert(std::is_trivially_destructible_v<T> && std::is_trivially_copy_constructible_v<T>);
30 [[nodiscard]]
31 static auto to_ptr(const T &value) noexcept -> void*
33 if constexpr(std::is_pointer_v<T>)
35 if constexpr(std::is_const_v<std::remove_pointer_t<T>>)
36 return const_cast<void*>(static_cast<const void*>(value)); /* NOLINT(*-const-cast) */
37 else
38 return static_cast<void*>(value);
40 else if constexpr(sizeof(T) == sizeof(void*))
41 return al::bit_cast<void*>(value);
42 else if constexpr(std::is_integral_v<T>)
43 return al::bit_cast<void*>(static_cast<std::uintptr_t>(value));
46 [[nodiscard]]
47 static auto from_ptr(void *ptr) noexcept -> T
49 if constexpr(std::is_pointer_v<T>)
50 return static_cast<T>(ptr);
51 else if constexpr(sizeof(T) == sizeof(void*))
52 return al::bit_cast<T>(ptr);
53 else if constexpr(std::is_integral_v<T>)
54 return static_cast<T>(al::bit_cast<std::uintptr_t>(ptr));
57 #ifdef _WIN32
58 DWORD mTss{TLS_OUT_OF_INDEXES};
60 public:
61 tss() : mTss{TlsAlloc()}
63 if(mTss == TLS_OUT_OF_INDEXES)
64 throw std::runtime_error{"al::tss::tss()"};
66 explicit tss(const T &init) : tss{}
68 if(TlsSetValue(mTss, to_ptr(init)) == FALSE)
69 throw std::runtime_error{"al::tss::tss(T)"};
71 ~tss() { TlsFree(mTss); }
73 void set(const T &value) const
75 if(TlsSetValue(mTss, to_ptr(value)) == FALSE)
76 throw std::runtime_error{"al::tss::set(T)"};
79 [[nodiscard]]
80 auto get() const noexcept -> T { return from_ptr(TlsGetValue(mTss)); }
82 #elif defined(__APPLE__)
84 pthread_key_t mTss{};
86 public:
87 tss()
89 if(int res{pthread_key_create(&mTss, nullptr)}; res != 0)
90 throw std::runtime_error{"al::tss::tss()"};
92 explicit tss(const T &init) : tss{}
94 if(int res{pthread_setspecific(mTss, to_ptr(init))}; res != 0)
95 throw std::runtime_error{"al::tss::tss(T)"};
97 ~tss() { pthread_key_delete(mTss); }
99 void set(const T &value) const
101 if(int res{pthread_setspecific(mTss, to_ptr(value))}; res != 0)
102 throw std::runtime_error{"al::tss::set(T)"};
105 [[nodiscard]]
106 auto get() const noexcept -> T { return from_ptr(pthread_getspecific(mTss)); }
108 #else
110 tss_t mTss{};
112 public:
113 tss()
115 if(int res{tss_create(&mTss, nullptr)}; res != thrd_success)
116 throw std::runtime_error{"al::tss::tss()"};
118 explicit tss(const T &init) : tss{}
120 if(int res{tss_set(mTss, to_ptr(init))}; res != thrd_success)
121 throw std::runtime_error{"al::tss::tss(T)"};
123 ~tss() { tss_delete(mTss); }
125 void set(const T &value) const
127 if(int res{tss_set(mTss, to_ptr(value))}; res != thrd_success)
128 throw std::runtime_error{"al::tss::set(T)"};
131 [[nodiscard]]
132 auto get() const noexcept -> T { return from_ptr(tss_get(mTss)); }
133 #endif /* _WIN32 */
135 tss(const tss&) = delete;
136 tss(tss&&) = delete;
137 void operator=(const tss&) = delete;
138 void operator=(tss&&) = delete;
141 } // namespace al
143 #endif /* AL_THREADS_H */