make coroutines immovable (moving invalidates inside pointers)
If you move a coroutine, all references to the coroutine from inside of it become invalid, including e.g. yielders. Therefore, make coroutines immovable to prevent weird bugs... but also, make the guts of coroutine context a bit more move friendly (like, do not change current status after context switch and always do it outside), in case a solution is found in the future.master
parent
77814ca08f
commit
493f31fabf
|
@ -96,36 +96,10 @@ protected:
|
||||||
virtual ~coroutine_context();
|
virtual ~coroutine_context();
|
||||||
|
|
||||||
coroutine_context(coroutine_context const &) = delete;
|
coroutine_context(coroutine_context const &) = delete;
|
||||||
|
coroutine_context(coroutine_context &&c) = delete;
|
||||||
/** @brief Moves the given context into this one.
|
|
||||||
*
|
|
||||||
* The other context is deinitialized and set as dead.
|
|
||||||
*
|
|
||||||
* @see operator=(coroutine_context &&)
|
|
||||||
*/
|
|
||||||
coroutine_context(coroutine_context &&c):
|
|
||||||
p_stack(std::move(c.p_stack)), p_coro(c.p_coro), p_orig(c.p_orig),
|
|
||||||
p_free(c.p_free), p_except(std::move(c.p_except)), p_state(c.p_state)
|
|
||||||
{
|
|
||||||
c.p_coro = c.p_orig = nullptr;
|
|
||||||
c.p_stack = { nullptr, 0 };
|
|
||||||
c.p_free = nullptr;
|
|
||||||
c.set_dead();
|
|
||||||
}
|
|
||||||
|
|
||||||
coroutine_context &operator=(coroutine_context const &) = delete;
|
coroutine_context &operator=(coroutine_context const &) = delete;
|
||||||
|
coroutine_context &operator=(coroutine_context &&c) = delete;
|
||||||
/** @brief Moves the given context into this one.
|
|
||||||
*
|
|
||||||
* Effectively calls swap(coroutine_context &). The current context
|
|
||||||
* is then destroyed by the other context's destructor.
|
|
||||||
*
|
|
||||||
* @returns Returns `*this*`.
|
|
||||||
*/
|
|
||||||
coroutine_context &operator=(coroutine_context &&c) {
|
|
||||||
swap(c);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief Calls the coroutine context.
|
/** @brief Calls the coroutine context.
|
||||||
*
|
*
|
||||||
|
@ -154,7 +128,9 @@ protected:
|
||||||
* @see yield_jump(), yield_done()
|
* @see yield_jump(), yield_done()
|
||||||
*/
|
*/
|
||||||
void coro_jump() {
|
void coro_jump() {
|
||||||
p_coro = detail::ostd_jump_fcontext(p_coro, this).ctx;
|
auto tfer = detail::ostd_jump_fcontext(p_coro, this);
|
||||||
|
p_coro = tfer.ctx;
|
||||||
|
p_state = state(std::size_t(tfer.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Jumps out of the coroutine context.
|
/** @brief Jumps out of the coroutine context.
|
||||||
|
@ -165,8 +141,10 @@ protected:
|
||||||
* @see coro_jump(), yield_done()
|
* @see coro_jump(), yield_done()
|
||||||
*/
|
*/
|
||||||
void yield_jump() {
|
void yield_jump() {
|
||||||
p_state = state::HOLD;
|
auto tfer = detail::ostd_jump_fcontext(
|
||||||
p_orig = detail::ostd_jump_fcontext(p_orig, nullptr).ctx;
|
p_orig, reinterpret_cast<void *>(std::size_t(state::HOLD))
|
||||||
|
);
|
||||||
|
static_cast<coroutine_context *>(tfer.data)->p_orig = tfer.ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Jumps out of the coroutine context.
|
/** @brief Jumps out of the coroutine context.
|
||||||
|
@ -177,8 +155,10 @@ protected:
|
||||||
* @see coro_jump(), yield_jump()
|
* @see coro_jump(), yield_jump()
|
||||||
*/
|
*/
|
||||||
void yield_done() {
|
void yield_done() {
|
||||||
set_dead();
|
auto tfer = detail::ostd_jump_fcontext(
|
||||||
p_orig = detail::ostd_jump_fcontext(p_orig, nullptr).ctx;
|
p_orig, reinterpret_cast<void *>(std::size_t(state::TERM))
|
||||||
|
);
|
||||||
|
static_cast<coroutine_context *>(tfer.data)->p_orig = tfer.ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Checks if the coroutine is suspended. */
|
/** @brief Checks if the coroutine is suspended. */
|
||||||
|
@ -275,8 +255,8 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
struct forced_unwind {
|
struct forced_unwind {
|
||||||
detail::fcontext_t ctx;
|
detail::transfer_t tfer;
|
||||||
forced_unwind(detail::fcontext_t c): ctx(c) {}
|
forced_unwind(detail::transfer_t t): tfer(t) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class state {
|
enum class state {
|
||||||
|
@ -295,9 +275,9 @@ private:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
detail::ostd_ontop_fcontext(
|
detail::ostd_ontop_fcontext(
|
||||||
std::exchange(p_coro, nullptr), nullptr,
|
std::exchange(p_coro, nullptr), this,
|
||||||
[](detail::transfer_t t) -> detail::transfer_t {
|
[](detail::transfer_t t) -> detail::transfer_t {
|
||||||
throw forced_unwind{t.ctx};
|
throw forced_unwind{t};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -325,19 +305,18 @@ private:
|
||||||
/* we never got to execute properly, we're HOLD because we
|
/* we never got to execute properly, we're HOLD because we
|
||||||
* jumped here without setting the state to EXEC before that
|
* jumped here without setting the state to EXEC before that
|
||||||
*/
|
*/
|
||||||
goto release;
|
self.yield_done();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
self.resume_call();
|
self.resume_call();
|
||||||
} catch (coroutine_context::forced_unwind v) {
|
} catch (coroutine_context::forced_unwind v) {
|
||||||
/* forced_unwind is unique */
|
/* forced_unwind is unique */
|
||||||
self.p_orig = v.ctx;
|
static_cast<C *>(v.tfer.data)->p_orig = v.tfer.ctx;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
/* some other exception, will be rethrown later */
|
/* some other exception, will be rethrown later */
|
||||||
self.p_except = std::current_exception();
|
self.p_except = std::current_exception();
|
||||||
}
|
}
|
||||||
/* switch back, release stack */
|
/* switch back, release stack */
|
||||||
release:
|
|
||||||
self.yield_done();
|
self.yield_done();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,30 +715,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
coroutine(coroutine const &) = delete;
|
coroutine(coroutine const &) = delete;
|
||||||
|
coroutine(coroutine &&c) = delete;
|
||||||
/** @brief Moves a coroutine.
|
|
||||||
*
|
|
||||||
* Moves the given coroutine's context and any other data into the new
|
|
||||||
* coroutine. The other coroutine is left in a dead state.
|
|
||||||
*
|
|
||||||
* @see operator=(coroutine &&)
|
|
||||||
*/
|
|
||||||
coroutine(coroutine &&c) noexcept:
|
|
||||||
base_t(std::move(c)), p_stor(std::move(c.p_stor))
|
|
||||||
{
|
|
||||||
c.p_stor.p_func = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
coroutine &operator=(coroutine const &) = delete;
|
coroutine &operator=(coroutine const &) = delete;
|
||||||
|
coroutine &operator=(coroutine &&c) = delete;
|
||||||
/** @brief Moves a coroutine.
|
|
||||||
*
|
|
||||||
* Effectively like a call to swap(coroutine &).
|
|
||||||
*/
|
|
||||||
coroutine &operator=(coroutine &&c) noexcept {
|
|
||||||
base_t::operator=(std::move(c));
|
|
||||||
p_stor.swap(c.p_stor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief Checks if the coroutine is alive. */
|
/** @brief Checks if the coroutine is alive. */
|
||||||
explicit operator bool() const noexcept {
|
explicit operator bool() const noexcept {
|
||||||
|
@ -987,34 +946,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
generator(generator const &) = delete;
|
generator(generator const &) = delete;
|
||||||
|
generator(generator &&c) = delete;
|
||||||
/** @brief Moves a generator.
|
|
||||||
*
|
|
||||||
* Moves the given generator's context and any other data into the new
|
|
||||||
* generator. The other generator is left in a dead state.
|
|
||||||
*
|
|
||||||
* @see operator=(generator &&)
|
|
||||||
*/
|
|
||||||
generator(generator &&c) noexcept:
|
|
||||||
base_t(std::move(c)), p_func(std::move(c.p_func)), p_result(c.p_result)
|
|
||||||
{
|
|
||||||
c.p_func = nullptr;
|
|
||||||
c.p_result = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
generator &operator=(generator const &) = delete;
|
generator &operator=(generator const &) = delete;
|
||||||
|
generator &operator=(generator &&c) = delete;
|
||||||
/** @brief Moves a generator.
|
|
||||||
*
|
|
||||||
* Effectively like a call to swap(generator &).
|
|
||||||
*/
|
|
||||||
generator &operator=(generator &&c) noexcept {
|
|
||||||
base_t::operator=(std::move(c));
|
|
||||||
p_func = std::move(c.p_func);
|
|
||||||
p_result = c.p_result;
|
|
||||||
c.p_func = nullptr;
|
|
||||||
c.p_result = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief Checks if the generator is alive. */
|
/** @brief Checks if the generator is alive. */
|
||||||
explicit operator bool() const noexcept {
|
explicit operator bool() const noexcept {
|
||||||
|
|
|
@ -12,3 +12,10 @@
|
||||||
#else
|
#else
|
||||||
# error "Unsupported platform"
|
# error "Unsupported platform"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace ostd {
|
||||||
|
struct coroutine_context;
|
||||||
|
namespace detail {
|
||||||
|
OSTD_EXPORT thread_local coroutine_context *coro_current = nullptr;
|
||||||
|
} /* namespace detail */
|
||||||
|
} /* namespace ostd */
|
||||||
|
|
|
@ -124,10 +124,4 @@ OSTD_EXPORT std::size_t stack_traits::default_size() noexcept {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct coroutine_context;
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
OSTD_EXPORT thread_local coroutine_context *coro_current = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* namespace ostd */
|
} /* namespace ostd */
|
||||||
|
|
|
@ -80,10 +80,4 @@ OSTD_EXPORT std::size_t stack_traits::default_size() noexcept {
|
||||||
return 8 * 8 * 1024;
|
return 8 * 8 * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct coroutine_context;
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
OSTD_EXPORT thread_local coroutine_context *coro_current = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* namespace ostd */
|
} /* namespace ostd */
|
||||||
|
|
Loading…
Reference in New Issue