mirror of
https://github.com/fergalmoran/ladybird.git
synced 2025-12-22 09:19:03 +00:00
Thread: Cleanup m_blocker handling
The only two places we set m_blocker now are Thread::set_state(), and
Thread::block(). set_state is mostly just an issue of clarity: we don't
want to end up with state() != Blocked with an m_blocker, because that's
weird. It's also possible: if we yield, someone else may set_state() us.
We also now set_state() and set m_blocker under lock in block(), rather
than unlocking which might allow someone else to mess with our internals
while we're in the process of trying to block.
This seems to fix sending STOP & CONT causing a panic.
My guess as to what was happening is this:
thread A blocks in select(): Blocking & m_blocker != nullptr
thread B sends SIGSTOP: Stopped & m_blocker != nullptr
thread B sends SIGCONT: we continue execution. Runnable & m_blocker != nullptr
thread A tries to block in select() again:
* sets m_blocker
* unlocks (in block_helper)
* someone else tries to unblock us? maybe from the old m_blocker? unclear -- clears m_blocker
* sets Blocked (while unlocked!)
So, thread A is left with state Blocked & m_blocker == nullptr, leading
to the scheduler assert (m_blocker != nullptr) failing.
Long story short, let's do all our data management with the lock _held_.
This commit is contained in:
committed by
Andreas Kling
parent
046f00f77e
commit
d48c73b10a
@@ -97,7 +97,6 @@ Thread::~Thread()
|
||||
|
||||
void Thread::unblock()
|
||||
{
|
||||
m_blocker = nullptr;
|
||||
if (current == this) {
|
||||
set_state(Thread::Running);
|
||||
return;
|
||||
@@ -108,9 +107,11 @@ void Thread::unblock()
|
||||
|
||||
void Thread::block_helper()
|
||||
{
|
||||
// This function mostly exists to avoid circular header dependencies. If
|
||||
// anything needs adding, think carefully about whether it belongs in
|
||||
// block() instead. Remember that we're unlocking here, so be very careful
|
||||
// about altering any state once we're unlocked!
|
||||
bool did_unlock = process().big_lock().unlock_if_locked();
|
||||
ASSERT(state() == Thread::Running);
|
||||
set_state(Thread::Blocked);
|
||||
Scheduler::yield();
|
||||
if (did_unlock)
|
||||
process().big_lock().lock();
|
||||
@@ -160,8 +161,6 @@ void Thread::finalize()
|
||||
dbgprintf("Finalizing Thread %u in %s(%u)\n", tid(), m_process.name().characters(), pid());
|
||||
set_state(Thread::State::Dead);
|
||||
|
||||
m_blocker = nullptr;
|
||||
|
||||
if (this == &m_process.main_thread())
|
||||
m_process.finalize();
|
||||
}
|
||||
@@ -539,6 +538,11 @@ bool Thread::is_thread(void* ptr)
|
||||
void Thread::set_state(State new_state)
|
||||
{
|
||||
InterruptDisabler disabler;
|
||||
if (new_state == Blocked) {
|
||||
// we should always have an m_blocker while blocked
|
||||
ASSERT(m_blocker != nullptr);
|
||||
}
|
||||
|
||||
m_state = new_state;
|
||||
if (m_process.pid() != 0) {
|
||||
SchedulerThreadList* list = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user