Skip to content

Instantly share code, notes, and snippets.

@jamboree
Last active August 29, 2015 14:06
Show Gist options
  • Save jamboree/57edf1e0dc6bf0b64e4e to your computer and use it in GitHub Desktop.
Save jamboree/57edf1e0dc6bf0b64e4e to your computer and use it in GitHub Desktop.
#include <iostream>
#include <exception>
#include <functional>
#include <memory>
#include <boost/context/all.hpp>
#include <boost/optional/optional.hpp>
struct forced_unwind {};
struct resumer_base
{
boost::context::fcontext_t master, slave;
resumer_base() : master(), slave() {}
resumer_base(resumer_base&& other)
: master(other.master), slave(other.slave)
{
other.slave = nullptr;
}
void exit(boost::intptr_t val)
{
slave = nullptr;
boost::context::jump_fcontext(&master, master, 0);
}
void unwind()
{
if (slave && master)
boost::context::jump_fcontext(&master, slave, 0);
}
};
template<class T>
struct yielder
{
using type = boost::optional<T>;
resumer_base& o;
void operator()(T&& val) const
{
if (!boost::context::jump_fcontext(&o.slave, o.master, reinterpret_cast<boost::intptr_t>(&val)))
throw forced_unwind{};
}
void operator()(T const& ref) const
{
T val(ref);
if (!boost::context::jump_fcontext(&o.slave, o.master, reinterpret_cast<boost::intptr_t>(&val)))
throw forced_unwind{};
}
static type ret(boost::intptr_t val)
{
return val? type{std::move(*reinterpret_cast<T*>(val))} : type{};
}
};
template<>
struct yielder<void>
{
using type = bool;
resumer_base& o;
void operator()() const
{
if (!boost::context::jump_fcontext(&o.slave, o.master, true))
throw forced_unwind{};
}
static type ret(bool val)
{
return val;
}
};
template<class R>
struct resumer : resumer_base
{
typename yielder<R>::type operator()()
{
if (slave)
{
auto val = boost::context::jump_fcontext(&master, slave, reinterpret_cast<boost::intptr_t>(this));
if (slave)
return yielder<R>::ret(val);
if (val)
{
auto& e = *reinterpret_cast<std::exception_ptr*>(val);
auto e2 = e;
e.~exception_ptr();
std::rethrow_exception(e2);
}
}
return {};
}
protected:
resumer() = default;
resumer(resumer&&) = default;
};
template<class R, class F>
struct resumable : resumer<R>, private F
{
using resumer<R>::operator();
resumable(resumable&&) = default;
resumable(void* stack, std::size_t size, F&& f)
: F(std::forward<F>(f))
{
using base = resumer_base;
base::slave = boost::context::make_fcontext(static_cast<char*>(stack) + size, size, [](boost::intptr_t p)
{
auto that = reinterpret_cast<resumable*>(p);
try
{
static_cast<F&>(*that)(yielder<R>{*that});
}
catch (forced_unwind&) {}
catch (...)
{
auto e = std::current_exception();
that->base::exit(reinterpret_cast<boost::intptr_t>(&e));
}
that->base::exit(0);
});
}
~resumable()
{
resumer_base::unwind();
}
};
template<class R, class F>
resumable<R, F> make_resumable(void* stack, std::size_t size, F&& f)
{
return {stack, size, std::forward<F>(f)};
}
template<class R, class F>
auto make_resumable(F&& f)
{
std::unique_ptr<char[]> stack(new char[1024 * 64]);
auto sp = stack.get();
return make_resumable<R>(sp, 1024 * 64, [s=std::move(stack), f=std::forward<F>(f)](auto yield){ f(yield); });
}
template<class F>
auto make_shared_resumable(F&& f)
{
struct context
{
char stack[1024 * 64];
resumable<void, F> resume;
context(F&& f) : resume(stack, 1024 * 64, std::forward<F>(f)) {}
};
return [ctx=std::make_shared<context>(std::forward<F>(f))]{ return ctx->resume(); };
}
int main(int argc, char** argv)
{
//char stack[1024 * 64];
//std::cout << reinterpret_cast<std::uintptr_t>(stack + 1024 * 8) << std::endl;
auto f = make_resumable<std::uintptr_t>([](auto yield)
{
std::cout << "----------------" << std::endl;
char a;
yield(reinterpret_cast<std::uintptr_t>(&a));
char b;
yield(reinterpret_cast<std::uintptr_t>(&b));
char c[100];
yield(reinterpret_cast<std::uintptr_t>(&c));
});
while (auto i = f())
std::cout << i.get() << std::endl;
std::function<bool()> r(make_shared_resumable([](auto yield)
{
std::cout << "a\n";
yield();
std::cout << "b\n";
}));
while (r());
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment