fix issues with coroutine scheduler threads locking up

master
Daniel Kolesa 2017-03-21 19:49:06 +01:00
parent 307123fd35
commit c1a1c4a1ac
1 changed files with 23 additions and 18 deletions

View File

@ -257,17 +257,11 @@ private:
func(); func();
} }
void yield(basic_coroutine_scheduler &sched) { void yield() {
{ /* we'll yield back to the thread we were scheduled to, which
std::lock_guard<std::mutex> l{sched.p_lock}; * will appropriately notify one or all other waiting threads
sched.p_available.splice( * so we either get re-scheduled or the whole scheduler dies
sched.p_available.cend(), sched.p_running, pos */
);
}
yield_raw();
}
void yield_raw() {
typename coro::yield_type{func}(); typename coro::yield_type{func}();
} }
@ -299,7 +293,7 @@ private:
l.unlock(); l.unlock();
task *curr = task::current(); task *curr = task::current();
p_sched.wait(this, p_waiting, curr); p_sched.wait(this, p_waiting, curr);
curr->yield_raw(); curr->yield();
l.lock(); l.lock();
} }
@ -378,7 +372,7 @@ public:
} }
void yield() { void yield() {
task::current()->yield(*this); task::current()->yield();
} }
template<typename T> template<typename T>
@ -403,10 +397,8 @@ private:
} }
void destroy() { void destroy() {
p_cond.notify_all();
for (auto &tid: p_thrs) { for (auto &tid: p_thrs) {
tid.join(); tid.join();
p_cond.notify_all();
} }
} }
@ -428,7 +420,7 @@ private:
wl = wl->next_waiting; wl = wl->next_waiting;
l.unlock(); l.unlock();
p_cond.notify_one(); p_cond.notify_one();
task::current()->yield(*this); task::current()->yield();
} }
void notify_all(task *&wl) { void notify_all(task *&wl) {
@ -443,7 +435,7 @@ private:
l.lock(); l.lock();
} }
} }
task::current()->yield(*this); task::current()->yield();
} }
void thread_run() { void thread_run() {
@ -470,8 +462,21 @@ private:
l.lock(); l.lock();
if (c.dead()) { if (c.dead()) {
p_running.erase(it); p_running.erase(it);
/* we're dead, notify all threads so they can be joined
* we check all three, saves the other threads some re-waiting
* when a task or tasks are already running, and those that do
* will do the final notify by themselves
*/
if (p_available.empty() && p_waiting.empty() && p_running.empty()) {
l.unlock();
p_cond.notify_all();
}
} else if (!c.waiting_on) {
/* reschedule to the end of the queue */
p_available.splice(p_available.cend(), p_running, it);
l.unlock();
p_cond.notify_one();
} }
l.unlock();
} }
std::condition_variable p_cond; std::condition_variable p_cond;