Serenity Operating System
at master 138 lines 4.8 kB view raw
1/* 2 * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibThreading/Thread.h> 8#include <pthread.h> 9#include <string.h> 10#include <unistd.h> 11 12namespace Threading { 13 14Thread::Thread(Function<intptr_t()> action, StringView thread_name) 15 : Core::Object(nullptr) 16 , m_action(move(action)) 17 , m_thread_name(thread_name.is_null() ? ""sv : thread_name) 18{ 19 register_property("thread_name", [&] { return JsonValue { m_thread_name }; }); 20#if defined(AK_OS_SERENITY) || defined(AK_OS_LINUX) 21 // FIXME: Print out a pretty TID for BSD and macOS platforms, too 22 register_property("tid", [&] { return JsonValue { m_tid }; }); 23#endif 24} 25 26Thread::~Thread() 27{ 28 if (needs_to_be_joined()) { 29 dbgln("Destroying {} while it is still running undetached!", *this); 30 [[maybe_unused]] auto res = join(); 31 } 32 if (m_state == ThreadState::Detached) 33 dbgln("Bug! {} in state {} is being destroyed; AK/Function will crash shortly!", *this, m_state.load()); 34} 35 36ErrorOr<void> Thread::set_priority(int priority) 37{ 38 // MacOS has an extra __opaque field, so list initialization will not compile on MacOS Lagom. 39 sched_param scheduling_parameters {}; 40 scheduling_parameters.sched_priority = priority; 41 int result = pthread_setschedparam(m_tid, 0, &scheduling_parameters); 42 if (result != 0) 43 return Error::from_errno(result); 44 return {}; 45} 46 47ErrorOr<int> Thread::get_priority() const 48{ 49 sched_param scheduling_parameters {}; 50 int policy; 51 int result = pthread_getschedparam(m_tid, &policy, &scheduling_parameters); 52 if (result != 0) 53 return Error::from_errno(result); 54 return scheduling_parameters.sched_priority; 55} 56 57DeprecatedString Thread::thread_name() const { return m_thread_name; } 58 59pthread_t Thread::tid() const { return m_tid; } 60 61ThreadState Thread::state() const { return m_state; } 62 63bool Thread::is_started() const { return m_state != ThreadState::Startable; } 64 65bool Threading::Thread::needs_to_be_joined() const 66{ 67 auto state = m_state.load(); 68 return state == ThreadState::Running || state == ThreadState::Exited; 69} 70 71bool Threading::Thread::has_exited() const 72{ 73 auto state = m_state.load(); 74 return state == ThreadState::Joined || state == ThreadState::Exited || state == ThreadState::DetachedExited; 75} 76 77void Thread::start() 78{ 79 VERIFY(!is_started()); 80 81 // Set this first so that the other thread starts out seeing m_state == Running. 82 m_state = Threading::ThreadState::Running; 83 84 int rc = pthread_create( 85 &m_tid, 86 // FIXME: Use pthread_attr_t to start a thread detached if that was requested by the user before the call to start(). 87 nullptr, 88 [](void* arg) -> void* { 89 Thread* self = static_cast<Thread*>(arg); 90 auto exit_code = self->m_action(); 91 92 auto expected = Threading::ThreadState::Running; 93 // This code might race with a call to detach(). 94 if (!self->m_state.compare_exchange_strong(expected, Threading::ThreadState::Exited)) { 95 // If the original state was Detached, we need to set to DetachedExited instead. 96 if (expected == Threading::ThreadState::Detached) { 97 if (!self->m_state.compare_exchange_strong(expected, Threading::ThreadState::DetachedExited)) { 98 dbgln("Thread logic bug: Found thread state {} while trying to set ExitedDetached state!", expected); 99 VERIFY_NOT_REACHED(); 100 } 101 } else { 102 dbgln("Thread logic bug: Found thread state {} while trying to set Exited state!", expected); 103 VERIFY_NOT_REACHED(); 104 } 105 } 106 107 return reinterpret_cast<void*>(exit_code); 108 }, 109 static_cast<void*>(this)); 110 111 VERIFY(rc == 0); 112#ifdef AK_OS_SERENITY 113 if (!m_thread_name.is_empty()) { 114 rc = pthread_setname_np(m_tid, m_thread_name.characters()); 115 VERIFY(rc == 0); 116 } 117#endif 118 dbgln("Started {}", *this); 119} 120 121void Thread::detach() 122{ 123 auto expected = Threading::ThreadState::Running; 124 // This code might race with the other thread exiting. 125 if (!m_state.compare_exchange_strong(expected, Threading::ThreadState::Detached)) { 126 // Always report a precise error before crashing. These kinds of bugs are hard to reproduce. 127 if (expected == Threading::ThreadState::Exited) 128 dbgln("Thread logic bug: {} is being detached after having exited!", this); 129 else 130 dbgln("Thread logic bug: trying to detach {} which is not in the Started state, but state {}!", this, expected); 131 VERIFY_NOT_REACHED(); 132 } 133 134 int rc = pthread_detach(m_tid); 135 VERIFY(rc == 0); 136} 137 138}