Boost logo

Boost :

Subject: [boost] [RFC][Patch][Boost.context] Allowing alternative context switch API
From: microcai (microcai_at_[hidden])
Date: 2014-12-08 03:18:47


Hi Oliver Kowalke, and others

last weak, I report a bug that boost.context causes LoadLibrary to fail.
And Oliver Kowalke response that might be because boost.context didn't swap
all TIB parts.

Allowing alternative context switch API other than reverse-engineered
boost.context might solve the problem. So I speed some time to write the
patch.

users that wish to use Win32 API shoul define BOOST_COROUTINE_USE_FIBER
then users should be responsibe to make sure all threads that might call
coroutine must be converted to fiber first.
since IsThreadAFIber() is not available on some windows platform.

if the user can make 100% sure that the platform he/she wish to support has
IsThreadAFIber(), then define BOOST_COROUTINE_USE_IS_THREAD_A_FIBER
and boost.coroutine will take care of converting to fiber and converting back
to thread.

Best regards.

---
>From 11668b6641c877a4ea154d51ad2171483eced921 Mon Sep 17 00:00:00 2001
From: microcai <microcaicai_at_[hidden]>
Date: Sat, 6 Dec 2014 15:50:46 +0800
Subject: [PATCH] allow Win32 Fiber as alternative context switch API
windows has some undocumented TIB parts, as boost.context can't keep
100% compatibility with all windows versions, and coroutine is essential
to many building blocks. It has to be stable, to allowing alternative
context switch API can be more use full.
---
 .../boost/coroutine/detail/coroutine_context.hpp   | 11 ++++
 .../coroutine/detail/pull_coroutine_object.hpp     |  9 ++++
 .../coroutine/detail/push_coroutine_object.hpp     |  9 ++++
 include/boost/coroutine/stack_allocator.hpp        |  5 ++
 .../coroutine/windows/null_stack_allocator.hpp     | 53 +++++++++++++++++++
 src/detail/coroutine_context.cpp                   | 60 
++++++++++++++++++++--
 6 files changed, 142 insertions(+), 5 deletions(-)
 create mode 100644 include/boost/coroutine/windows/null_stack_allocator.hpp
diff --git a/include/boost/coroutine/detail/coroutine_context.hpp 
b/include/boost/coroutine/detail/coroutine_context.hpp
index 6c4ebb6..f6a53e2 100644
--- a/include/boost/coroutine/detail/coroutine_context.hpp
+++ b/include/boost/coroutine/detail/coroutine_context.hpp
@@ -16,6 +16,9 @@
 #include <boost/coroutine/detail/config.hpp>
 #include <boost/coroutine/stack_context.hpp>
 
+#ifdef BOOST_COROUTINE_USE_FIBER
+# include <windows.h>
+#endif
 #ifdef BOOST_HAS_ABI_HEADERS
 #  include BOOST_ABI_PREFIX
 #endif
@@ -31,6 +34,12 @@ class BOOST_COROUTINES_DECL coroutine_context
 private:
     stack_context           stack_ctx_;
     context::fcontext_t     ctx_;
+#ifdef BOOST_COROUTINE_USE_FIBER
+    void                    (*fn_)(intptr_t);
+    intptr_t                param_;
+    LPVOID                  fiber_;
+	static VOID WINAPI fb_start_proc(LPVOID lpFiberParameter);
+#endif // BOOST_COROUTINE_USE_FIBER
 
 public:
     typedef void( * ctx_fn)( intptr_t);
@@ -51,6 +60,8 @@ public:
 
     stack_context & stack_ctx()
     { return stack_ctx_; }
+
+	void destory();
 };
 
 }}}
diff --git a/include/boost/coroutine/detail/pull_coroutine_object.hpp 
b/include/boost/coroutine/detail/pull_coroutine_object.hpp
index cfe34bd..d15d94f 100644
--- a/include/boost/coroutine/detail/pull_coroutine_object.hpp
+++ b/include/boost/coroutine/detail/pull_coroutine_object.hpp
@@ -65,6 +65,9 @@ private:
         stack_context stack_ctx( obj->stack_ctx_);
         StackAllocator stack_alloc( obj->stack_alloc_);
         obj->unwind_stack();
+#ifdef BOOST_COROUTINE_USE_FIBER
+        obj->callee.destory();
+#endif
         obj->~obj_t();
         stack_alloc.deallocate( stack_ctx);
     }
@@ -153,6 +156,9 @@ private:
         stack_context stack_ctx( obj->stack_ctx_);
         StackAllocator stack_alloc( obj->stack_alloc_);
         obj->unwind_stack();
+#ifdef BOOST_COROUTINE_USE_FIBER
+        obj->callee.destory();
+#endif
         obj->~obj_t();
         stack_alloc.deallocate( stack_ctx);
     }
@@ -241,6 +247,9 @@ private:
         stack_context stack_ctx( obj->stack_ctx_);
         StackAllocator stack_alloc( obj->stack_alloc_);
         obj->unwind_stack();
+#ifdef BOOST_COROUTINE_USE_FIBER
+        obj->callee.destory();
+#endif
         obj->~obj_t();
         stack_alloc.deallocate( stack_ctx);
     }
diff --git a/include/boost/coroutine/detail/push_coroutine_object.hpp 
b/include/boost/coroutine/detail/push_coroutine_object.hpp
index 1a39c07..b41a3b2 100644
--- a/include/boost/coroutine/detail/push_coroutine_object.hpp
+++ b/include/boost/coroutine/detail/push_coroutine_object.hpp
@@ -77,6 +77,9 @@ private:
         stack_context stack_ctx( obj->stack_ctx_);
         StackAllocator stack_alloc( obj->stack_alloc_);
         obj->unwind_stack();
+#ifdef BOOST_COROUTINE_USE_FIBER
+        obj->callee.destory();
+#endif
         obj->~obj_t();
         stack_alloc.deallocate( stack_ctx);
     }
@@ -165,6 +168,9 @@ private:
         stack_context stack_ctx( obj->stack_ctx_);
         StackAllocator stack_alloc( obj->stack_alloc_);
         obj->unwind_stack();
+#ifdef BOOST_COROUTINE_USE_FIBER
+        obj->callee.destory();
+#endif
         obj->~obj_t();
         stack_alloc.deallocate( stack_ctx);
     }
@@ -253,6 +259,9 @@ private:
         stack_context stack_ctx( obj->stack_ctx_);
         StackAllocator stack_alloc( obj->stack_alloc_);
         obj->unwind_stack();
+#ifdef BOOST_COROUTINE_USE_FIBER
+        obj->callee.destory();
+#endif
         obj->~obj_t();
         stack_alloc.deallocate( stack_ctx);
     }
diff --git a/include/boost/coroutine/stack_allocator.hpp 
b/include/boost/coroutine/stack_allocator.hpp
index 662533e..811f352 100644
--- a/include/boost/coroutine/stack_allocator.hpp
+++ b/include/boost/coroutine/stack_allocator.hpp
@@ -14,6 +14,9 @@
 #include <boost/context/detail/config.hpp>
 #include <boost/coroutine/segmented_stack_allocator.hpp>
 #include <boost/coroutine/standard_stack_allocator.hpp>
+#if defined(BOOST_WINDOWS)
+# include <boost/coroutine/windows/null_stack_allocator.hpp>
+#endif
 
 #ifdef BOOST_HAS_ABI_HEADERS
 #  include BOOST_ABI_PREFIX
@@ -24,6 +27,8 @@ namespace coroutines {
 
 #if defined(BOOST_USE_SEGMENTED_STACKS)
 typedef segmented_stack_allocator   stack_allocator;
+#elif defined(BOOST_COROUTINE_USE_FIBER)
+typedef null_stack_allocator    stack_allocator;
 #else
 typedef standard_stack_allocator    stack_allocator;
 #endif
diff --git a/include/boost/coroutine/windows/null_stack_allocator.hpp 
b/include/boost/coroutine/windows/null_stack_allocator.hpp
new file mode 100644
index 0000000..25f9b34
--- /dev/null
+++ b/include/boost/coroutine/windows/null_stack_allocator.hpp
@@ -0,0 +1,53 @@
+
+//          Copyright Oliver Kowalke 2009.
+// Distributed under the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE_1_0.txt or copy at
+//          http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef BOOST_COROUTINES_NULL_STACK_ALLOCATOR_H
+#define BOOST_COROUTINES_NULL_STACK_ALLOCATOR_H
+
+extern "C" {
+#include <windows.h>
+}
+
+#include <cmath>
+#include <cstddef>
+#include <new>
+
+#include <boost/config.hpp>
+
+#include <boost/coroutine/detail/config.hpp>
+#include <boost/coroutine/stack_traits.hpp>
+
+#ifdef BOOST_HAS_ABI_HEADERS
+#  include BOOST_ABI_PREFIX
+#endif
+
+namespace boost {
+namespace coroutines {
+
+struct stack_context;
+
+template< typename traitsT >
+struct basic_null_stack_allocator
+{
+    typedef traitsT traits_type;
+
+    void allocate(stack_context & ctx, std::size_t size)
+    {}
+
+    void deallocate(stack_context & ctx)
+    {}
+};
+
+typedef basic_null_stack_allocator< stack_traits > null_stack_allocator;
+
+}
+}
+
+#ifdef BOOST_HAS_ABI_HEADERS
+#  include BOOST_ABI_SUFFIX
+#endif
+
+#endif // BOOST_COROUTINES_NULL_STACK_ALLOCATOR_H
diff --git a/src/detail/coroutine_context.cpp 
b/src/detail/coroutine_context.cpp
index f63479e..e021281 100644
--- a/src/detail/coroutine_context.cpp
+++ b/src/detail/coroutine_context.cpp
@@ -28,6 +28,14 @@ void __splitstack_setcontext( void * 
[BOOST_COROUTINES_SEGMENTS]);
 namespace boost {
 namespace coroutines {
 namespace detail {
+#ifdef BOOST_COROUTINE_USE_FIBER
+VOID WINAPI coroutine_context::fb_start_proc(LPVOID lpFiberParameter)
+{
+    coroutine_context * ctx = (coroutine_context*)lpFiberParameter;
+    ctx->fn_(ctx->param_);
+    DeleteFiber(ctx->fiber_);
+}
+#endif
 
 coroutine_context::coroutine_context() :
     stack_ctx_(),
@@ -35,18 +43,35 @@ coroutine_context::coroutine_context() :
 {
 #if defined(BOOST_USE_SEGMENTED_STACKS)
     __splitstack_getcontext( stack_ctx_.segments_ctx);
+#elif defined(BOOST_COROUTINE_USE_FIBER)
+    fiber_ = NULL;
+    param_ = 0;
+    fn_ = 0;
 #endif
 }
 
 coroutine_context::coroutine_context( ctx_fn fn, stack_context const& 
stack_ctx) :
-    stack_ctx_( stack_ctx),
-    ctx_( context::make_fcontext( stack_ctx_.sp, stack_ctx_.size, fn) )
-{}
+    stack_ctx_( stack_ctx)
+#ifndef BOOST_COROUTINE_USE_FIBER
+    , ctx_(context::make_fcontext( stack_ctx_.sp, stack_ctx_.size, fn) )
+#endif // !BOOST_COROUTINE_USE_FIBER
+{
+#ifdef BOOST_COROUTINE_USE_FIBER
+    fn_ = fn;
+    fiber_ = CreateFiber(0, fb_start_proc, this);
+#endif // BOOST_COROUTINE_USE_FIBER
+}
 
 coroutine_context::coroutine_context( coroutine_context const& other) :
     stack_ctx_( other.stack_ctx_),
     ctx_( other.ctx_)
-{}
+{
+#ifdef BOOST_COROUTINE_USE_FIBER
+    real_fn = other.real_fn;
+    fiber_ = other.fiber_;
+    param_ = other.param_;
+#endif // BOOST_COROUTINE_USE_FIBER
+}
 
 coroutine_context &
 coroutine_context::operator=( coroutine_context const& other)
@@ -55,7 +80,11 @@ coroutine_context::operator=( coroutine_context const& 
other)
 
     stack_ctx_ = other.stack_ctx_;
     ctx_ = other.ctx_;
-
+#ifdef BOOST_COROUTINE_USE_FIBER
+    real_fn = other.real_fn;
+    fiber_ = other.fiber_;
+    param_ = other.param_;
+#endif // BOOST_COROUTINE_USE_FIBER
     return * this;
 }
 
@@ -71,11 +100,32 @@ coroutine_context::jump( coroutine_context & other, 
intptr_t param, bool preserv
     __splitstack_setcontext( stack_ctx_.segments_ctx);
 
     return ret;
+#elif BOOST_COROUTINE_USE_FIBER
+    other.param_ = param;
+#ifdef BOOST_COROUTINE_USE_IS_THREAD_A_FIBER
+    if (!IsThreadAFiber())
+    {
+        this->fiber_ = ConvertThreadToFiber(this);
+        SwitchToFiber(other.fiber_);
+        ConvertFiberToThread();
+        this->fiber_ = NULL;
+    }
+    else
+#endif // BOOST_COROUTINE_USE_IS_THREAD_A_FIBER
+        SwitchToFiber(other.fiber_);
+    return this->param_;
 #else
     return context::jump_fcontext( & ctx_, other.ctx_, param, preserve_fpu);
 #endif
 }
 
+void coroutine_context::destory()
+{
+#ifdef BOOST_COROUTINE_USE_FIBER
+    DeleteFiber(fiber_);
+#endif
+}
+
 }}}
 
 #if defined(_MSC_VER)
-- 
2.2.0

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk