fixes, bigger stack for main task, take spawn funcs by value

This commit is contained in:
q66 2017-03-22 17:31:57 +01:00
parent c1a1c4a1ac
commit 2537d955d1
4 changed files with 71 additions and 49 deletions

View file

@ -10,6 +10,7 @@
#include <utility> #include <utility>
#include <memory> #include <memory>
#include "ostd/platform.hh"
#include "ostd/coroutine.hh" #include "ostd/coroutine.hh"
#include "ostd/channel.hh" #include "ostd/channel.hh"
@ -29,7 +30,7 @@ struct thread_scheduler {
} }
template<typename F, typename ...A> template<typename F, typename ...A>
void spawn(F &&func, A &&...args) { void spawn(F func, A &&...args) {
std::lock_guard<std::mutex> l{p_lock}; std::lock_guard<std::mutex> l{p_lock};
p_threads.emplace_front(); p_threads.emplace_front();
auto it = p_threads.begin(); auto it = p_threads.begin();
@ -38,7 +39,7 @@ struct thread_scheduler {
func(std::move(args)...); func(std::move(args)...);
remove_thread(it); remove_thread(it);
}, },
std::forward<F>(func), std::forward<A>(args)... std::move(func), std::forward<A>(args)...
}; };
} }
@ -125,7 +126,7 @@ public:
p_stacks(ss, cs), p_stacks(ss, cs),
p_dispatcher([this](auto yield_main) { p_dispatcher([this](auto yield_main) {
this->dispatch(yield_main); this->dispatch(yield_main);
}, p_stacks.get_allocator()), }),
p_coros() p_coros()
{} {}
@ -143,14 +144,14 @@ public:
} }
template<typename F, typename ...A> template<typename F, typename ...A>
void spawn(F &&func, A &&...args) { void spawn(F func, A &&...args) {
if constexpr(sizeof...(A) == 0) { if constexpr(sizeof...(A) == 0) {
p_coros.emplace_back([lfunc = std::forward<F>(func)](auto) { p_coros.emplace_back([lfunc = std::move(func)](auto) {
lfunc(); lfunc();
}, p_stacks.get_allocator()); }, p_stacks.get_allocator());
} else { } else {
p_coros.emplace_back([lfunc = std::bind( p_coros.emplace_back([lfunc = std::bind(
std::forward<F>(func), std::forward<A>(args)... std::move(func), std::forward<A>(args)...
)](auto) mutable { )](auto) mutable {
lfunc(); lfunc();
}, p_stacks.get_allocator()); }, p_stacks.get_allocator());
@ -323,50 +324,39 @@ public:
~basic_coroutine_scheduler() {} ~basic_coroutine_scheduler() {}
template<typename F, typename ...A> template<typename F, typename ...A>
auto start(F &&func, A &&...args) -> std::result_of_t<F(A...)> { auto start(F func, A &&...args) -> std::result_of_t<F(A...)> {
/* start with one task in the queue, this way we can /* start with one task in the queue, this way we can
* say we've finished when the task queue becomes empty * say we've finished when the task queue becomes empty
*/ */
using R = std::result_of_t<F(A...)>; using R = std::result_of_t<F(A...)>;
/* the default 64 KiB stack won't cut it for main, allocate a stack
* which matches the size of the process stack outside of the pool
*/
basic_fixedsize_stack<TR, Protected> sa{detail::stack_main_size()};
if constexpr(std::is_same_v<R, void>) { if constexpr(std::is_same_v<R, void>) {
spawn(std::forward<F>(func), std::forward<A>(args)...); spawn_add(sa, std::move(func), std::forward<A>(args)...);
/* actually start the thread pool */ /* actually start the thread pool */
init(); init();
destroy();
} else { } else {
R ret; R ret;
spawn([&ret, func = std::forward<F>(func)](auto &&...args) { spawn_add(sa, [&ret, func = std::move(func)](auto &&...args) {
ret = func(std::forward<A>(args)...); ret = func(std::forward<A>(args)...);
}, std::forward<A>(args)...); }, std::forward<A>(args)...);
init(); init();
destroy();
return ret; return ret;
} }
} }
template<typename F, typename ...A> template<typename F, typename ...A>
void spawn(F &&func, A &&...args) { void spawn(F func, A &&...args) {
{ {
std::lock_guard<std::mutex> l{p_lock}; std::lock_guard<std::mutex> l{p_lock};
task *t = nullptr; spawn_add(
if constexpr(sizeof...(A) == 0) { p_stacks.get_allocator(), std::move(func),
t = &p_available.emplace_back( std::forward<A>(args)...
[lfunc = std::forward<F>(func)](auto) {
lfunc();
},
p_stacks.get_allocator()
); );
} else {
t = &p_available.emplace_back(
[lfunc = std::bind(
std::forward<F>(func), std::forward<A>(args)...
)](auto) mutable {
lfunc();
},
p_stacks.get_allocator()
);
}
t->pos = --p_available.end();
} }
p_cond.notify_one(); p_cond.notify_one();
} }
@ -382,23 +372,40 @@ public:
}}; }};
} }
private: private:
void init() { template<typename SA, typename F, typename ...A>
auto tf = [this]() { void spawn_add(SA &&sa, F &&func, A &&...args) {
thread_run(); task *t = nullptr;
}; if constexpr(sizeof...(A) == 0) {
size_t size = std::thread::hardware_concurrency(); t = &p_available.emplace_back(
for (size_t i = 0; i < size; ++i) { [lfunc = std::forward<F>(func)](auto) {
std::thread tid{tf}; lfunc();
if (!tid.joinable()) { },
throw std::runtime_error{"coroutine_scheduler worker failed"}; std::forward<SA>(sa)
} );
p_thrs.push_back(std::move(tid)); } else {
t = &p_available.emplace_back(
[lfunc = std::bind(
std::forward<F>(func), std::forward<A>(args)...
)](auto) mutable {
lfunc();
},
std::forward<SA>(sa)
);
} }
t->pos = --p_available.end();
} }
void destroy() { void init() {
for (auto &tid: p_thrs) { size_t size = std::thread::hardware_concurrency();
tid.join(); std::vector<std::thread> thrs;
thrs.reserve(size);
for (size_t i = 0; i < size; ++i) {
thrs.emplace_back([this]() { thread_run(); });
}
for (size_t i = 0; i < size; ++i) {
if (thrs[i].joinable()) {
thrs[i].join();
}
} }
} }
@ -481,7 +488,6 @@ private:
std::condition_variable p_cond; std::condition_variable p_cond;
std::mutex p_lock; std::mutex p_lock;
std::vector<std::thread> p_thrs;
basic_stack_pool<TR, Protected> p_stacks; basic_stack_pool<TR, Protected> p_stacks;
tlist p_available; tlist p_available;
tlist p_waiting; tlist p_waiting;

View file

@ -43,6 +43,7 @@ namespace detail {
OSTD_EXPORT void *stack_alloc(size_t sz); OSTD_EXPORT void *stack_alloc(size_t sz);
OSTD_EXPORT void stack_free(void *p, size_t sz) noexcept; OSTD_EXPORT void stack_free(void *p, size_t sz) noexcept;
OSTD_EXPORT void stack_protect(void *p, size_t sz) noexcept; OSTD_EXPORT void stack_protect(void *p, size_t sz) noexcept;
OSTD_EXPORT size_t stack_main_size() noexcept;
} }
template<typename TR, bool Protected> template<typename TR, bool Protected>
@ -50,7 +51,11 @@ struct basic_fixedsize_stack {
using traits_type = TR; using traits_type = TR;
basic_fixedsize_stack(size_t ss = TR::default_size()) noexcept: basic_fixedsize_stack(size_t ss = TR::default_size()) noexcept:
p_size(std::clamp(ss, TR::minimum_size(), TR::maximum_size())) p_size(
TR::is_unbounded()
? std::max(ss, TR::minimum_size())
: std::clamp(ss, TR::minimum_size(), TR::maximum_size())
)
{} {}
stack_context allocate() { stack_context allocate() {

View file

@ -219,8 +219,8 @@ release:
} }
stack_context p_stack; stack_context p_stack;
detail::fcontext_t p_coro; detail::fcontext_t p_coro = nullptr;
detail::fcontext_t p_orig; detail::fcontext_t p_orig = nullptr;
std::exception_ptr p_except; std::exception_ptr p_except;
state p_state = state::HOLD; state p_state = state::HOLD;
}; };

View file

@ -74,6 +74,17 @@ namespace detail {
#endif #endif
} }
OSTD_EXPORT size_t stack_main_size() noexcept {
#if defined(OSTD_PLATFORM_WIN32)
/* 4 MiB for windows... */
return 4 * 1024 * 1024;
#else
struct rlimit l;
getrlimit(RLIMIT_STACK, &l);
return size_t(l.rlim_cur);
#endif
}
OSTD_EXPORT void stack_protect(void *p, size_t sz) noexcept { OSTD_EXPORT void stack_protect(void *p, size_t sz) noexcept {
#if defined(OSTD_PLATFORM_WIN32) #if defined(OSTD_PLATFORM_WIN32)
DWORD oo; DWORD oo;