I have copied the essence from the unit test and put it into my Visual Studio (VS2013 Version 12.31101.00 Update4) environment, but still crashes. The project is a natvie C++ project (not mixed mode .NET) and in "Configuration Properties/C/C++/Code Generation/Enable C++ Exceptions" is set to "Yes(/EHsc)". However there is another option : "Yes with SEH Exceptions (/EHa)", but does not seem to fix the issue.

Meanwhile I have implemented a workaround (see below): for each context I create a thread. As soon as this new thread is started I switch it to another context (created by boost::context::make_fcontext) and block it there on a condition variable. Then I use the original stack of the thread. This "hijacked" stack can cope with exceptions, just as I would expect. When I want to release the context then I just simply release the blocked thread and switch it back to its original context. This way I can make sure that the stack of the thread is also correctly unwound.

#include "stdafx.h"
#include "boost/context/all.hpp"
#include <boost/assert.hpp>
#include "CppUnitTest.h"
#include <iostream>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

template< std::size_t Max, std::size_t Default, std::size_t Min >
class simple_stack_allocator
{
public:
        static std::size_t maximum_stacksize()
        {
                return Max;
        }

        static std::size_t default_stacksize()
        {
                return Default;
        }

        static std::size_t minimum_stacksize()
        {
                return Min;
        }

        void * allocate(std::size_t size) const
        {
                BOOST_ASSERT(minimum_stacksize() <= size);
                BOOST_ASSERT(maximum_stacksize() >= size);

                void * limit = std::malloc(size);
                if (!limit) throw std::bad_alloc();

                return static_cast< char * >(limit)+size;
        }

        void deallocate(void * vp, std::size_t size) const
        {
                BOOST_ASSERT(vp);
                BOOST_ASSERT(minimum_stacksize() <= size);
                BOOST_ASSERT(maximum_stacksize() >= size);

                void * limit = static_cast< char * >(vp)-size;
                std::free(limit);
        }
};

void fException()
{
        std::cout << "I am still ok" << std::endl; // Executes well
        throw std::runtime_error("crash!"); // Execution crashes at this point
}

boost::context::fcontext_t fcm, fc1;

void f1(intptr_t)
{
        try
        {
                std::cout << "Greetings from context f1" << std::endl;                        // Executes well
                fException();
                std::cout << "Never reach this" << std::endl;                // Break point here does not hit
        }
        catch (std::runtime_error const& e)
        {
                std::cout << "runtime_error exception caught" << std::endl;                 // Break point here does not hit
        }
        catch (...)
        {
                std::cout << "General exception caught" << std::endl;                // Break point here does not hit
        }

        boost::context::jump_fcontext(&fc1, fcm, 0);         // Break point here does not hit
}

namespace ctx = boost::context;

typedef simple_stack_allocator<
        8 * 1024 * 1024, // 8MB
        64 * 1024, // 64kB
        8 * 1024 // 8kB
>       stack_allocator;

void test()
{
        stack_allocator alloc;
        void * stackPointer = alloc.allocate(stack_allocator::default_stacksize());

        fc1 = boost::context::make_fcontext(stackPointer, stack_allocator::minimum_stacksize(), f1);
        std::cout << "Greetings from main thread stack" << std::endl;
        boost::context::jump_fcontext(&fcm, fc1, 0);
}


TEST_CLASS(BoostContextTest)
{
public:
        TEST_METHOD(ExceptionContextTest)
        {
                test();
        }
};


// ======================================================================================================================================================================================


// This class starts a thread and switches that thread to a context where it is parking.
// Meanwhile we can use the original stack of the thread. This way the stack of the thread temporarily hijacked
// The reason why the stack of a real thread is needed for the context is the exception handling.
// The context that boost::context makes does not work with exceptions
class HijackedThread
{
public:
        HijackedThread(std::size_t stackSize, boost::context::fcontext_t& contextHandle)
                : _threadParkingContextStack(new ContextStack(64 * 1024))
                , _threadContextHandle(contextHandle)
                , _threadParkingContextHandle(nullptr)
                , _threadInParkingContext(false)
                , _releaseParkingThread(false)
        {
                CreateAndParkThread(stackSize);

                WaitForThreadToPark();
        }

        ~HijackedThread()
        {
                ReleaseParkingThread();
        }

        bool IsMethodSet()
        {
                auto isMethodSet = (bool)_method;
                return isMethodSet;
        }

        void SetMethod(std::function<void()> method)
        {
                _method = method;
        }

private:

        void CreateAndParkThread(std::size_t stackSize)
        {
                _threadParkingContextHandle = boost::context::make_fcontext(_threadParkingContextStack->Bottom(), _threadParkingContextStack->Size(), ParkingContextStartup);

                boost::thread::attributes attrs;
                attrs.set_stack_size(stackSize);

                _thread = new boost::thread(attrs, [=]
                {
                        boost::context::jump_fcontext(&_threadContextHandle, _threadParkingContextHandle, reinterpret_cast<intptr_t>(this));

                        if (_threadInParkingContext)
                        {
                                _method();
                        }
                });
        }

        static void ParkingContextStartup(intptr_t instance)
        {
                HijackedThread* hijackedThread = reinterpret_cast<HijackedThread*>(instance);

                hijackedThread->ParkThread();

                // Wind up the context stack from the thread
                boost::context::jump_fcontext(&(hijackedThread->_threadParkingContextHandle), hijackedThread->_threadContextHandle, 0);
        }

        void ParkThread()
        {
                boost::mutex::scoped_lock lock(_mutex);

                _threadInParkingContext = true;
                _monitor.notify_all();

                while (!_releaseParkingThread)
                {
                        _monitor.wait(lock);
                }

                _threadInParkingContext = false;
        }

        void WaitForThreadToPark()
        {
                boost::mutex::scoped_lock lock(_mutex);
                while (!_threadInParkingContext)
                {
                        _monitor.wait(lock);
                }
        }

        void UnParkThread()
        {
                boost::mutex::scoped_lock lock(_mutex);
                _releaseParkingThread = true;
                _monitor.notify_all();
        }

        void ReleaseParkingThread()
        {
                UnParkThread();
                _thread->join();
        }

private:
        boost::mutex _mutex;
        boost::condition_variable _monitor;
        boost::thread* _thread;

        boost::context::fcontext_t& _threadContextHandle;

        boost::context::fcontext_t _threadParkingContextHandle;
        std::unique_ptr<ContextStack> _threadParkingContextStack;

        std::function<void()> _method;

        bool _threadInParkingContext;
        bool _releaseParkingThread;
};

// FIXME: Only with shared_ptr? Why not with move constructor?
class Context : public std::enable_shared_from_this<Context>
{
public:

        ~Context()
        {
                UnwindStack();
        }

        static std::shared_ptr<Context> CreateNew(std::size_t stackSize)
        {
                auto newContext = std::shared_ptr<Context>(new Context(stackSize));
                return newContext;
        }

        static std::shared_ptr<Context> CreateFromCurrentStack()
        {
                auto newContext = std::shared_ptr<Context>(new Context());
                return newContext;
        }

        void SetName(const std::string& name)
        {
                _name = name;
        }

        void SetMethod(std::function<void(Context*)> method)
        {
                if (!_hijackedThread)
                {
                        throw ContextChainException("Cannot set a Startup method for a thread");
                }

                if (_hijackedThread->IsMethodSet())
                {
                        throw ContextChainException("Startup method can be set only once");
                }

                _hijackedThread->SetMethod([=]
                {
                        method(this);

                        _methodFinished = true;

                        // When the method is done, then yield back
                        Return();
                });
        }

        void SwitchSafe(const std::shared_ptr<Context>& newContext)
        {
                if (_unwinding)
                        return;

                if (_hijackedThread && !(_hijackedThread->IsMethodSet()))
                {
                        throw ContextChainException("No startup method defined");
                }

                // The destination context shall be not involved in any switching chain
                // otherwise the way back to the thread stack will be messed up
                if (newContext->_returnContext)
                {
                        throw ContextChainException("Recursion in context is not allowed");
                }

                // In the newContext the method must be not finished, otherwise it makes no
                // sense to switch to it
                if (newContext->_methodFinished)
                {
                        throw ContextFinishedException("Context method has already finished");
                }

                Switch(newContext);
        }

        void Switch(const std::shared_ptr<Context>& newContext)
        {
                if (_unwinding)
                        return;

                newContext->_returnContext = shared_from_this();
                boost::context::jump_fcontext(&_contextHandle, newContext->_contextHandle, 0);
        }

        void ReturnSafe()
        {
                if (_unwinding)
                        return;

                // There must be a place to return
                if (!_returnContext)
                {
                        throw ContextChainException("No place to return");
                }

                Return();
        }

        void Return()
        {
                if (_unwinding)
                        return;

                auto returnContextHandle = _returnContext->_contextHandle;
                _returnContext.reset();
                boost::context::jump_fcontext(&_contextHandle, returnContextHandle, 0);
        }

protected:
        Context()
                : _hijackedThread(nullptr)
                , _contextHandle(nullptr)
                , _returnContext(nullptr)
                , _methodFinished(false)
                , _unwinding(false)
        {
        }

        Context(std::size_t stackSize)
                : _hijackedThread(nullptr)
                , _contextHandle(nullptr)
                , _returnContext(nullptr)
                , _methodFinished(false)
                , _unwinding(false)
        {
                _hijackedThread.reset(new HijackedThread(stackSize, _contextHandle));
        }

        void UnwindStack()
        {
                _unwinding = true;
                // Let the hijacked thread unwind the context
                _hijackedThread.reset();
        }

protected:
        std::unique_ptr<HijackedThread> _hijackedThread;
        boost::context::fcontext_t _contextHandle;
        std::shared_ptr<Context> _returnContext;
        bool _methodFinished;
        bool _unwinding;

        std::string _name;
};




From:        Oliver Kowalke <oliver.kowalke@gmail.com>
To:        boost-users <boost-users@lists.boost.org>,
Date:        14.03.2015 10:12
Subject:        Re: [Boost-users] [context] Crash in case of exception from a        context
Sent by:        "Boost-users" <boost-users-bounces@lists.boost.org>




2015-03-14 6:37 GMT+01:00 Nathaniel Fries <nfries88@gmail.com>:
The solution is actually pretty simple - push the current SEH list's head onto the stack before the context switch, and restore it when resuming execution, with each context initializing its own list with UnhandledExceptionFilter during either construction or first run (like a new thread would). But maybe such a fix is out of scope for the context library?l

boost.context already installs SEH structures on the stack - the unit-tests of boost.context already check throwing/catching exceptions ->
http://www.boost.org/development/tests/master/developer/context.html
http://www.boost.org/development/tests/develop/developer/context.html_______________________________________________
Boost-users mailing list
Boost-users@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users