1 // Copyright (c) 2006, Giovanni P. Deretta 2 // 3 // This code may be used under either of the following two licences: 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. OF SUCH DAMAGE. 22 // 23 // Or: 24 // 25 // Distributed under the Boost Software License, Version 1.0. 26 // (See accompanying file LICENSE_1_0.txt or copy at 27 // http://www.boost.org/LICENSE_1_0.txt) 28 29 #ifndef HPX_RUNTIME_THREADS_COROUTINES_DETAIL_CONTEXT_WINDOWS_HPP 30 #define HPX_RUNTIME_THREADS_COROUTINES_DETAIL_CONTEXT_WINDOWS_HPP 31 32 #include <windows.h> 33 #include <winnt.h> 34 35 #include <hpx/config.hpp> 36 #include <hpx/runtime/threads/coroutines/detail/swap_context.hpp> 37 #include <hpx/runtime/threads/coroutines/exception.hpp> 38 #include <hpx/util/assert.hpp> 39 #include <hpx/util/get_and_reset_value.hpp> 40 #include <hpx/util/unused.hpp> 41 42 #include <boost/system/error_code.hpp> 43 #include <boost/system/system_error.hpp> 44 45 #include <atomic> 46 #include <cstddef> 47 #include <cstdint> 48 49 #if defined(HPX_HAVE_SWAP_CONTEXT_EMULATION) 50 extern "C" void switch_to_fiber(void* lpFiber) throw(); 51 #endif 52 53 namespace hpx { namespace threads { namespace coroutines 54 { 55 // On Windows we need a special preparation for the main coroutines thread 56 struct prepare_main_thread 57 { prepare_main_threadhpx::threads::coroutines::prepare_main_thread58 prepare_main_thread() 59 { 60 LPVOID result = ConvertThreadToFiber(nullptr); 61 HPX_ASSERT(nullptr != result); 62 HPX_UNUSED(result); 63 } 64 ~prepare_main_threadhpx::threads::coroutines::prepare_main_thread65 ~prepare_main_thread() 66 { 67 BOOL result = ConvertFiberToThread(); 68 HPX_ASSERT(FALSE != result); 69 HPX_UNUSED(result); 70 } 71 }; 72 73 namespace detail { namespace windows 74 { 75 typedef LPVOID fiber_ptr; 76 77 #if _WIN32_WINNT < 0x0600 78 /* 79 * This number (0x1E00) has been sighted in the wild (at least on 80 * windows XP systems) as return value from GetCurrentFiber() on non 81 * fibrous threads. 82 * This is somehow related to OS/2 where the current fiber pointer is 83 * overloaded as a version field. 84 * On non-NT systems, 0 is returned. 85 */ 86 fiber_ptr const fiber_magic = reinterpret_cast<fiber_ptr>(0x1E00); 87 #endif 88 89 /* 90 * Return true if current thread is a fiber. 91 */ is_fiber()92 inline bool is_fiber() 93 { 94 #if _WIN32_WINNT >= 0x0600 95 return IsThreadAFiber() ? true : false; 96 #else 97 fiber_ptr current = GetCurrentFiber(); 98 return current != nullptr && current != fiber_magic; 99 #endif 100 } 101 102 /* 103 * Windows implementation for the context_impl_base class. 104 * @note context_impl is not required to be consistent 105 * If not initialized it can only be swapped out, not in 106 * (at that point it will be initialized). 107 */ 108 class fibers_context_impl_base : detail::context_impl_base 109 { 110 public: 111 /** 112 * Create an empty context. 113 * An empty context cannot be restored from, 114 * but can be saved in. 115 */ fibers_context_impl_base()116 fibers_context_impl_base() : m_ctx(nullptr) {} 117 118 /* 119 * Free function. Saves the current context in @p from 120 * and restores the context in @p to. On windows the from 121 * parameter is ignored. The current context is saved on the 122 * current fiber. 123 * Note that if the current thread is not a fiber, it will be 124 * converted to fiber on the fly on call and unconverted before 125 * return. This is expensive. The user should convert the 126 * current thread to a fiber once on thread creation for better performance. 127 * Note that we can't leave the thread unconverted on return or else we 128 * will leak resources on thread destruction. Do the right thing by 129 * default. 130 */ swap_context(fibers_context_impl_base & from,const fibers_context_impl_base & to,default_hint)131 friend void swap_context(fibers_context_impl_base& from, 132 const fibers_context_impl_base& to, default_hint) 133 { 134 if (!is_fiber()) 135 { 136 HPX_ASSERT(from.m_ctx == nullptr); 137 from.m_ctx = ConvertThreadToFiber(nullptr); 138 HPX_ASSERT(from.m_ctx != nullptr); 139 140 #if defined(HPX_HAVE_SWAP_CONTEXT_EMULATION) 141 switch_to_fiber(to.m_ctx); 142 #else 143 SwitchToFiber(to.m_ctx); 144 #endif 145 BOOL result = ConvertFiberToThread(); 146 HPX_ASSERT(result); 147 HPX_UNUSED(result); 148 from.m_ctx = nullptr; 149 } else { 150 bool call_from_main = from.m_ctx == nullptr; 151 if (call_from_main) 152 from.m_ctx = GetCurrentFiber(); 153 #if defined(HPX_HAVE_SWAP_CONTEXT_EMULATION) 154 switch_to_fiber(to.m_ctx); 155 #else 156 SwitchToFiber(to.m_ctx); 157 #endif 158 if (call_from_main) 159 from.m_ctx = nullptr; 160 } 161 } 162 ~fibers_context_impl_base()163 ~fibers_context_impl_base() {} 164 165 protected: fibers_context_impl_base(fiber_ptr ctx)166 explicit fibers_context_impl_base(fiber_ptr ctx) 167 : m_ctx(ctx) 168 {} 169 170 fiber_ptr m_ctx; 171 }; 172 173 template <typename T> trampoline(LPVOID pv)174 HPX_FORCEINLINE VOID CALLBACK trampoline(LPVOID pv) 175 { 176 T* fun = static_cast<T*>(pv); 177 HPX_ASSERT(fun); 178 (*fun)(); 179 } 180 181 // initial stack size (grows as needed) 182 static const std::size_t stack_size = sizeof(void*) >= 8 ? 2048 : 1024; 183 184 class fibers_context_impl 185 : public fibers_context_impl_base 186 { 187 public: 188 HPX_NON_COPYABLE(fibers_context_impl); 189 190 public: 191 typedef fibers_context_impl_base context_impl_base; 192 193 enum { default_stack_size = stack_size }; 194 195 /** 196 * Create a context that on restore invokes Functor on 197 * a new stack. The stack size can be optionally specified. 198 */ 199 template<typename Functor> fibers_context_impl(Functor & cb,std::ptrdiff_t stack_size)200 explicit fibers_context_impl(Functor& cb, std::ptrdiff_t stack_size) 201 : fibers_context_impl_base( 202 CreateFiberEx(stack_size == -1 ? default_stack_size : stack_size, 203 stack_size == -1 ? default_stack_size : stack_size, 0, 204 static_cast<LPFIBER_START_ROUTINE>(&trampoline<Functor>), 205 static_cast<LPVOID>(&cb)) 206 ), 207 stacksize_(stack_size == -1 ? default_stack_size : stack_size) 208 { 209 if (nullptr == m_ctx) 210 { 211 throw boost::system::system_error( 212 boost::system::error_code( 213 GetLastError(), 214 boost::system::system_category() 215 ) 216 ); 217 } 218 } 219 ~fibers_context_impl()220 ~fibers_context_impl() 221 { 222 if (m_ctx != nullptr) 223 DeleteFiber(m_ctx); 224 } 225 226 // Return the size of the reserved stack address space. get_stacksize() const227 std::ptrdiff_t get_stacksize() const 228 { 229 return stacksize_; 230 } 231 reset_stack()232 void reset_stack() 233 { 234 } 235 rebind_stack()236 void rebind_stack() 237 { 238 increment_stack_recycle_count(); 239 } 240 241 typedef std::atomic<std::int64_t> counter_type; 242 get_stack_recycle_counter()243 static counter_type& get_stack_recycle_counter() 244 { 245 static counter_type counter(0); 246 return counter; 247 } 248 get_stack_recycle_count(bool reset)249 static std::uint64_t get_stack_recycle_count(bool reset) 250 { 251 return util::get_and_reset_value(get_stack_recycle_counter(), reset); 252 } 253 increment_stack_recycle_count()254 static std::uint64_t increment_stack_recycle_count() 255 { 256 return ++get_stack_recycle_counter(); 257 } 258 259 // global functions to be called for each OS-thread after it started 260 // running and before it exits thread_startup(char const * thread_type)261 static void thread_startup(char const* thread_type) {} thread_shutdown()262 static void thread_shutdown() {} 263 264 private: 265 std::ptrdiff_t stacksize_; 266 }; 267 268 typedef fibers_context_impl context_impl; 269 }} 270 }}} 271 272 #endif /*HPX_RUNTIME_THREADS_COROUTINES_DETAIL_CONTEXT_WINDOWS_HPP*/ 273