|
@@ -25,10 +25,12 @@ Thread::Thread(Function<intptr_t()> action, StringView thread_name)
|
|
|
|
|
|
Thread::~Thread()
|
|
Thread::~Thread()
|
|
{
|
|
{
|
|
- if (m_tid && !m_detached) {
|
|
|
|
- dbgln("Destroying thread \"{}\"({}) while it is still running!", m_thread_name, m_tid);
|
|
|
|
|
|
+ if (needs_to_be_joined()) {
|
|
|
|
+ dbgln("Destroying {} while it is still running undetached!", *this);
|
|
[[maybe_unused]] auto res = join();
|
|
[[maybe_unused]] auto res = join();
|
|
}
|
|
}
|
|
|
|
+ if (m_state == ThreadState::Detached)
|
|
|
|
+ dbgln("Bug! {} in state {} is being destroyed; AK/Function will crash shortly!", *this, m_state.load());
|
|
}
|
|
}
|
|
|
|
|
|
ErrorOr<void> Thread::set_priority(int priority)
|
|
ErrorOr<void> Thread::set_priority(int priority)
|
|
@@ -56,17 +58,52 @@ DeprecatedString Thread::thread_name() const { return m_thread_name; }
|
|
|
|
|
|
pthread_t Thread::tid() const { return m_tid; }
|
|
pthread_t Thread::tid() const { return m_tid; }
|
|
|
|
|
|
-bool Thread::is_started() const { return m_started; }
|
|
|
|
|
|
+ThreadState Thread::state() const { return m_state; }
|
|
|
|
+
|
|
|
|
+bool Thread::is_started() const { return m_state != ThreadState::Startable; }
|
|
|
|
+
|
|
|
|
+bool Threading::Thread::needs_to_be_joined() const
|
|
|
|
+{
|
|
|
|
+ auto state = m_state.load();
|
|
|
|
+ return state == ThreadState::Running || state == ThreadState::Exited;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool Threading::Thread::has_exited() const
|
|
|
|
+{
|
|
|
|
+ auto state = m_state.load();
|
|
|
|
+ return state == ThreadState::Joined || state == ThreadState::Exited || state == ThreadState::DetachedExited;
|
|
|
|
+}
|
|
|
|
|
|
void Thread::start()
|
|
void Thread::start()
|
|
{
|
|
{
|
|
|
|
+ VERIFY(!is_started());
|
|
|
|
+
|
|
|
|
+ // Set this first so that the other thread starts out seeing m_state == Running.
|
|
|
|
+ m_state = Threading::ThreadState::Running;
|
|
|
|
+
|
|
int rc = pthread_create(
|
|
int rc = pthread_create(
|
|
&m_tid,
|
|
&m_tid,
|
|
|
|
+ // FIXME: Use pthread_attr_t to start a thread detached if that was requested by the user before the call to start().
|
|
nullptr,
|
|
nullptr,
|
|
[](void* arg) -> void* {
|
|
[](void* arg) -> void* {
|
|
Thread* self = static_cast<Thread*>(arg);
|
|
Thread* self = static_cast<Thread*>(arg);
|
|
auto exit_code = self->m_action();
|
|
auto exit_code = self->m_action();
|
|
- self->m_tid = 0;
|
|
|
|
|
|
+
|
|
|
|
+ auto expected = Threading::ThreadState::Running;
|
|
|
|
+ // This code might race with a call to detach().
|
|
|
|
+ if (!self->m_state.compare_exchange_strong(expected, Threading::ThreadState::Exited)) {
|
|
|
|
+ // If the original state was Detached, we need to set to DetachedExited instead.
|
|
|
|
+ if (expected == Threading::ThreadState::Detached) {
|
|
|
|
+ if (!self->m_state.compare_exchange_strong(expected, Threading::ThreadState::DetachedExited)) {
|
|
|
|
+ dbgln("Thread logic bug: Found thread state {} while trying to set ExitedDetached state!", expected);
|
|
|
|
+ VERIFY_NOT_REACHED();
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ dbgln("Thread logic bug: Found thread state {} while trying to set Exited state!", expected);
|
|
|
|
+ VERIFY_NOT_REACHED();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
return reinterpret_cast<void*>(exit_code);
|
|
return reinterpret_cast<void*>(exit_code);
|
|
},
|
|
},
|
|
static_cast<void*>(this));
|
|
static_cast<void*>(this));
|
|
@@ -78,18 +115,24 @@ void Thread::start()
|
|
VERIFY(rc == 0);
|
|
VERIFY(rc == 0);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
- dbgln("Started thread \"{}\", tid = {}", m_thread_name, m_tid);
|
|
|
|
- m_started = true;
|
|
|
|
|
|
+ dbgln("Started {}", *this);
|
|
}
|
|
}
|
|
|
|
|
|
void Thread::detach()
|
|
void Thread::detach()
|
|
{
|
|
{
|
|
- VERIFY(!m_detached);
|
|
|
|
|
|
+ auto expected = Threading::ThreadState::Running;
|
|
|
|
+ // This code might race with the other thread exiting.
|
|
|
|
+ if (!m_state.compare_exchange_strong(expected, Threading::ThreadState::Detached)) {
|
|
|
|
+ // Always report a precise error before crashing. These kinds of bugs are hard to reproduce.
|
|
|
|
+ if (expected == Threading::ThreadState::Exited)
|
|
|
|
+ dbgln("Thread logic bug: {} is being detached after having exited!", this);
|
|
|
|
+ else
|
|
|
|
+ dbgln("Thread logic bug: trying to detach {} which is not in the Started state, but state {}!", this, expected);
|
|
|
|
+ VERIFY_NOT_REACHED();
|
|
|
|
+ }
|
|
|
|
|
|
int rc = pthread_detach(m_tid);
|
|
int rc = pthread_detach(m_tid);
|
|
VERIFY(rc == 0);
|
|
VERIFY(rc == 0);
|
|
-
|
|
|
|
- m_detached = true;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|