4

I want to implement a job system for my game engine using fibers. After searching the internet for a good c++ implementation of fibers, I found that Boost.Context is a good starting point.

Update 1: I want to implement my own scheduling algorithm, thus Boost.Fiber, Boost.Coroutine, Boost.Coroutine2 are not suitable for my implementation.

After compiling boost for x64 architecture and trying to run the basic example from the boost documentation I got the following exception:

boost::context::detail::forced_unwind at memory location

This is the code I tried to run (Visual Studio 2015 enterprise edition, windows 7):

#include <iostream>
#include <boost\context\continuation.hpp>
namespace ctx=boost::context;
int main()
{
    ctx::continuation source=ctx::callcc
    (
        [](ctx::continuation && sink)
        {
            int a=0;
            int b=1;
            for(;;)
            {
                sink=sink.resume(a);
                auto next=a+b;
                a=b;
                b=next;
            }
            return std::move(sink);
        }
    );
    for (int j=0;j<10;++j) {
        std::cout << source.get_data<int>() << " ";
        source=source.resume();
    }
    return 0;
}

The code ran correctly (correct output: 0 1 1 2 3 5 8 13 21 34 55), but when it finished the run I got the exception.

Update 2: The exception occurs only for the release build

I want to ask two questions regarding boost context:

1) What caused the stack unwinding exception, and how to avoid it?

2) I found the boost documentation a bit shallow, and couldn't find any other tutorial on how to use boost context. Can you direct me to some good sources/tutorials about boost context?

DontCareBear
  • 825
  • 2
  • 11
  • 25
  • Do not use `std::move` on return in your case – Arkady Aug 08 '17 at 13:23
  • What is the alternative? I got the same exception for all the examples in the boost documentation (even the ones that don't use std::move). – DontCareBear Aug 08 '17 at 13:30
  • 2
    I didn't answered on your question, because I don't know answer. I just remind you to not use `std::move` in this case, it will not help and may just slow down execution time. – Arkady Aug 08 '17 at 13:42
  • Do you propose to pass it (sink) by reference instead? In other words return &sink instead of std::move(sink)? – DontCareBear Aug 08 '17 at 13:45
  • https://en.wikipedia.org/wiki/Return_value_optimization – Arkady Aug 08 '17 at 14:02
  • @Arkady you're right in general, but MSVC++ is a bit standards challenged – sehe Aug 08 '17 at 14:32
  • @Arkady oops I was still mistaken. MSVC++ has required std::move for locals for a long time (might have been fixed). You're right in general, except this is not a local: it's a parameter passed in by reference and _requires_ the cast back to rvalue in the return statement. – sehe Aug 08 '17 at 14:59
  • @Arkady: contiunation is movable-only - gcc-6.30/clang-3.8 generate compiler error: error: call to deleted constructor of 'ctx::continuation'. – xlrg Aug 09 '17 at 09:26

3 Answers3

0

First off, the forced unwind exception is documented (and is what gives the context RAII support).

Secondly, if you wish for Fibers built upon Boost Context, use the libraries that exist:

Update:

Testing with Boost 1.64.0 on Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x64 I have not been able to reproduce the behaviour:

enter image description here

sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    First. I want to implement my own scheduling algorithm, so boost fiber\coroutine are not suitable for my job system. Second. I know its documented but my question was how to avoid it. – DontCareBear Aug 08 '17 at 14:15
  • Ok. Fair enough. You might want to add information to that extent to the question. (It's not mentioned/immediately obvious) – sehe Aug 08 '17 at 14:17
  • 1
    Updated my question. – DontCareBear Aug 08 '17 at 14:23
  • @DontCareBear Updated my answer. No repro on VS2015/update 3 for me. Could be a fixed issue in either Boost or MSVC – sehe Aug 08 '17 at 14:42
  • Wandbox suggests that [GCC](https://wandbox.org/permlink/0tGI6eZUFiJBk2sp) nor [Clang](https://wandbox.org/permlink/2hUdWZ07EvHbo3qk) have any issue with it – sehe Aug 08 '17 at 15:03
  • Maybe my visual studio installation is faulty (visual studio 2015 enterprise edition)? I will try to install it again, I had a lot of problems with the installation in the past, maybe this is the root of the problem. I will update my question accordingly as closed. – DontCareBear Aug 08 '17 at 15:51
  • OK, the same problem arises again. I installed the community edition of visual studio 2017. It happens after the destructor of continuation is called (after you press any key). It happens only in the release version of the exe. – DontCareBear Aug 09 '17 at 01:36
  • The destructor is of course pressed _before_ you press any key (that's not in your code) and YES! I can reproduce it with a release build. (Not for [GCC](https://wandbox.org/permlink/nRhsWeFxD8xALLV0) or [Clang](https://wandbox.org/permlink/6HBdIAWSs4zKzB0f)). Let me see – sehe Aug 09 '17 at 08:28
  • 1
    Indeed, the unhandled unwind exception occurs before reaching `std::cout << "Done\n";` in this sample: http://paste.ubuntu.com/25275525/. That's good news because it should make it tractible for the maintainer (Kowalke?) to trace to an issue. Could still be a compiler issue (e.g. if the compiler eliminates a non-trivial destructor somewhere). Good luck! – sehe Aug 09 '17 at 08:37
  • @DontCareBear boost.fiber allwos you to implement your own scheduling algorithm – xlrg Aug 09 '17 at 09:10
  • @sehe: I can't reproduce the error with gcc/clang/intel/msvc/mingw/msvc-14.1 on Linux/Windows 10. Boost's regression tests don't show an error - see http://www.boost.org/development/tests/develop/developer/context.html – xlrg Aug 09 '17 at 09:20
  • Thank you @sehe! from what I have seen boost fiber won't allow my to replace the default run queue. I want to implement the scheduling with wait free algorithms, thus I need to implement my own run queue (or find a wait free variant which I found). I prefer to only use pre made context switch, and roll the rest myself. Additionally I don't know if boost can coexist with the c++ atomics library (std atomic). I want maximum freedom with my implementation, thus not depending on the overhead of boost's implementation. – DontCareBear Aug 09 '17 at 13:22
  • 1
    @DontCareBear: you can use another run-queue implementation, take a look at examples/work_sharing.cpp or include/boost/fiber/algo/work_stealing.hpp (spmc-queue). – xlrg Aug 10 '17 at 05:49
  • 1
    @DontCareBear: you can use std:.atomics together with boost (grep for atomic in bosot.fiber's resources). – xlrg Aug 10 '17 at 05:49
  • @DontCareBear: what's the overhead of boost's implementation? – xlrg Aug 10 '17 at 05:51
  • @xlrg I don't know what's the overhead, this is the reason I prefer to roll my own. But the examples you showed me are great I will try it. – DontCareBear Aug 10 '17 at 12:03
  • @xlrg, I checked boost fiber. It does not answer to what I need. As you can see in my original question and a few comments ago, I want to implement my own fiber scheduler from the grounds up in a wait free manner. Boost fiber implements its own scheduler, they only let the user custom a few things around this scheduler, thus it's not what I ask for. As in my original question I only want to understand how to use boost context without running into internal bugs, and roll my scheduler around it. If you know about something similar to boost context that does work, it will be a huge help. – DontCareBear Aug 11 '17 at 00:10
0

After return 0, the source object is not in the position which be destroyed.

Something like:

namespace ctx = boost::context;
int a;
bool stop = false;
{
    ctx::continuation source = ctx::callcc(
        [&stop, &a](ctx::continuation && sink) {
        a = 0;
        int b = 1;
        for (;;) {
            sink = sink.resume();
            if (stop)
                break;
            int next = a + b;
            a = b;
            b = next;
        }
        return std::move(sink);
    });
    for (int j = 0; j < 10; ++j) {
        std::cout << a << " ";
        source = source.resume();
    }
    stop = true;
    source.resume();
}
ChrisT
  • 1
  • Could you clarify the statement "the source object is not in the position which be destroyed" a bit? I can't figure out what it means. – sehe Jun 07 '18 at 15:16
  • after sink = sink.resume(), the stack may contain live objects and they have to be destroyed correctly. – ChrisT Aug 03 '18 at 19:09
0

If you are running on Windows and the error only occurs in Release mode, you may need to check that your compiler flags are set appropriately.

From here:

Windows using fcontext_t: turn off global program optimization (/GL) and change /EHsc (compiler assumes that functions declared as extern "C" never throw a C++ exception) to /EHs (tells compiler assumes that functions declared as extern "C" may throw an exception).