Skip to content

Commit

Permalink
Avoid switching cothreads during construction
Browse files Browse the repository at this point in the history
  • Loading branch information
CzB404 committed Aug 27, 2024
1 parent 0773347 commit 211f0a7
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 32 deletions.
1 change: 0 additions & 1 deletion include/co.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ class thread
std::unique_ptr<thread> main;
const thread* current_active = nullptr;
const thread* current_thread = nullptr;
thread* current_this = nullptr;
std::exception_ptr current_exception;

thread_status() noexcept;
Expand Down
32 changes: 7 additions & 25 deletions include/co.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ inline const thread& active() noexcept

inline cothread_t thread::get_thread() const noexcept
{
return *this ? m_thread.get() : nullptr;
return m_thread.get();
}

inline size_t thread::get_stack_size() const noexcept
Expand All @@ -67,7 +67,6 @@ inline void thread::set_stack_size(size_t stack_size) noexcept

inline void thread::set_parent(const thread& parent) noexcept
{
assert(parent);
m_parent = &parent;
}

Expand Down Expand Up @@ -172,11 +171,6 @@ inline thread::thread(thread::entry_t entry, size_t stack_size, const thread& pa

inline void thread::setup()
{
assert(tl_status->current_this == nullptr);
assert(tl_status->current_thread == nullptr);
// Parameters to the true libco entry function have to be passed as "globals".
tl_status->current_this = this;
tl_status->current_thread = &active();
if (!m_thread)
{
auto cothread = cothread_t{};
Expand All @@ -191,13 +185,9 @@ inline void thread::setup()
}
if (m_thread == nullptr)
{
tl_status->current_this = nullptr;
tl_status->current_thread = nullptr;
throw thread_create_failure();
}
// Handing execution over to the entry wrapper to let it store the parameters on its stack.
tl_status->current_active = this;
co_switch(m_thread.get());
}

inline thread::operator bool() const noexcept
Expand All @@ -209,36 +199,28 @@ inline void thread::entry_wrapper() noexcept
{
while (true) // Reuse
{
assert(tl_status->current_this != nullptr);
assert(tl_status->current_thread != nullptr);
// Acquire parameters from setup
auto* self = std::exchange(tl_status->current_this, nullptr);
auto&& creating_thread = *std::exchange(tl_status->current_thread, nullptr);
self->m_active = true;
co::active().m_active = true;
try
{
// Hand control back to the cothread that called setup.
// This has to be done in the try block in case this cothread is stopped before use.
creating_thread.switch_to();
(self->m_entry)();
co::active().m_entry();
// Handling entry function return
throw thread_return_failure();
}
catch (const thread_stopping&)
{
assert(tl_status->current_thread != nullptr);
auto&& stopping_thread = *std::exchange(tl_status->current_thread, nullptr);
self->m_active = false;
co::active().m_active = false;
tl_status->current_active = &stopping_thread;
co_switch(stopping_thread.get_thread()); // Stop
}
catch (...)
{
assert(tl_status->current_exception == nullptr);
tl_status->current_exception = std::current_exception();
self->m_active = false;
tl_status->current_active = self->m_parent;
co_switch(self->m_parent->get_thread()); // Failure
co::active().m_active = false;
tl_status->current_active = co::active().m_parent;
co_switch(tl_status->current_active->get_thread()); // Failure
}
}
}
Expand Down
25 changes: 19 additions & 6 deletions test/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,20 @@ TEST_F(cppco, default_construct)
TEST_F(cppco, create_and_destroy)
{
EXPECT_CALL(libco_mock::api::get(), create(_, _));
EXPECT_CALL(libco_mock::api::get(), switch_to(_)).Times(4); // 2 in constructor, 2 in destructor
EXPECT_CALL(libco_mock::api::get(), delete_this(_));
co::thread cothread([]() {});
}

TEST_F(cppco, create_and_reset)
{
EXPECT_CALL(libco_mock::api::get(), create(_, _));
EXPECT_CALL(libco_mock::api::get(), switch_to(_)).Times(4); // 2 in constructor, 2 in reset
EXPECT_CALL(libco_mock::api::get(), switch_to(_)).Times(4); // 2 in test, 2 in reset
EXPECT_CALL(libco_mock::api::get(), delete_this(_));
auto cothread = co::thread([]() {});
auto cothread = co::thread([]()
{
co::active().get_parent().switch_to();
});
cothread.switch_to(); // Activate it
cothread.reset();
}

Expand Down Expand Up @@ -112,7 +115,7 @@ TEST_F(cppco, destructors)

{
EXPECT_CALL(libco_mock::api::get(), create(_, _)).Times(1);
EXPECT_CALL(libco_mock::api::get(), switch_to(_)).Times(6); // 2 in main, 2 in constructor, 2 in reset
EXPECT_CALL(libco_mock::api::get(), switch_to(_)).Times(4); // 2 in test, 2 in reset
auto& parent = co::active();
auto cothread = co::thread([&parent, &destructed]()
{
Expand Down Expand Up @@ -235,10 +238,16 @@ TEST_F(cppco, rewind)
TEST_F(cppco, rewind_after_exception)
{
class Dummy {};
auto cothread = co::thread([]() { throw Dummy{}; });
auto cothread = co::thread([]()
{
co::active().get_parent().switch_to();
throw Dummy{};
});
cothread.switch_to(); // Activate it
EXPECT_THROW(cothread.switch_to(), Dummy);
EXPECT_FALSE(cothread);
cothread.rewind();
cothread.switch_to(); // Activate it
EXPECT_TRUE(cothread);
EXPECT_THROW(cothread.switch_to(), Dummy);
}
Expand All @@ -257,7 +266,11 @@ TEST_F(cppco, set_stack_size)
TEST_F(cppco, set_stack_size_when_active)
{
EXPECT_CALL(::libco_mock::api::get(), create(co::thread::default_stack_size, _)).Times(1);
auto cothread = co::thread([]() {});
auto cothread = co::thread([]()
{
co::active().get_parent().switch_to();
});
cothread.switch_to(); // Activate it
EXPECT_TRUE(cothread);
EXPECT_EQ(cothread.get_stack_size(), co::thread::default_stack_size);
EXPECT_CALL(::libco_mock::api::get(), create(2 * co::thread::default_stack_size, _)).Times(1);
Expand Down

0 comments on commit 211f0a7

Please sign in to comment.