Serenity Operating System
at master 138 lines 4.4 kB view raw
1/* 2 * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> 3 * Copyright (c) 2021, Spencer Dixon <spencercdixon@gmail.com> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#pragma once 9 10#include <AK/Assertions.h> 11#include <AK/DeprecatedString.h> 12#include <AK/DistinctNumeric.h> 13#include <AK/Function.h> 14#include <AK/Result.h> 15#include <LibCore/Object.h> 16#include <pthread.h> 17 18namespace Threading { 19 20AK_TYPEDEF_DISTINCT_ORDERED_ID(intptr_t, ThreadError); 21 22// States of userspace threads are simplified over actual kernel states (and possibly POSIX states). 23// There are only a couple of well-defined transitions between these states, and any attempt to call a function in a state where this is not allowed will crash the program. 24enum class ThreadState : u8 { 25 // Thread has been constructed but not started. 26 // Transitions to Running via start(). 27 Startable, 28 // Thread has been started, might be running, and can be joined. 29 // Note that join() (valid to call in this state) only changes the thread state after the thread has exited, so it only ever transitions from Exited to Joined. 30 // Transitions to Detached via detach(), transitions to Exited when the thread finishes its action function. 31 Running, 32 // Thread has not been detached and exited, and has to still be joined. 33 // Transitions to Joined via join(). 34 Exited, 35 // Thread has been started but also detached, meaning it cannot be joined. 36 // Transitions to DetachedExited when the thread finishes its action function. 37 Detached, 38 // Thread has exited but was detached, meaning it cannot be joined. 39 DetachedExited, 40 // Thread has exited and been joined. 41 Joined, 42}; 43 44class Thread final : public Core::Object { 45 C_OBJECT(Thread); 46 47public: 48 virtual ~Thread(); 49 50 ErrorOr<void> set_priority(int priority); 51 ErrorOr<int> get_priority() const; 52 53 // Only callable in the Startable state. 54 void start(); 55 // Only callable in the Running state. 56 void detach(); 57 58 // Only callable in the Running or Exited states. 59 template<typename T = void> 60 Result<T, ThreadError> join(); 61 62 DeprecatedString thread_name() const; 63 pthread_t tid() const; 64 ThreadState state() const; 65 bool is_started() const; 66 bool needs_to_be_joined() const; 67 bool has_exited() const; 68 69private: 70 explicit Thread(Function<intptr_t()> action, StringView thread_name = {}); 71 Function<intptr_t()> m_action; 72 pthread_t m_tid { 0 }; 73 DeprecatedString m_thread_name; 74 Atomic<ThreadState> m_state { ThreadState::Startable }; 75}; 76 77template<typename T> 78Result<T, ThreadError> Thread::join() 79{ 80 VERIFY(needs_to_be_joined()); 81 82 void* thread_return = nullptr; 83 int rc = pthread_join(m_tid, &thread_return); 84 if (rc != 0) { 85 return ThreadError { rc }; 86 } 87 88 // The other thread has now stopped running, so a TOCTOU bug is not possible. 89 // (If you call join from two different threads, you're doing something *very* wrong anyways.) 90 VERIFY(m_state == ThreadState::Exited); 91 m_state = ThreadState::Joined; 92 93 if constexpr (IsVoid<T>) 94 return {}; 95 else 96 return { static_cast<T>(thread_return) }; 97} 98 99} 100 101template<> 102struct AK::Formatter<Threading::Thread> : AK::Formatter<FormatString> { 103 ErrorOr<void> format(FormatBuilder& builder, Threading::Thread const& thread) 104 { 105 return Formatter<FormatString>::format(builder, "Thread \"{}\"({})"sv, thread.thread_name(), thread.tid()); 106 } 107}; 108 109template<> 110struct AK::Formatter<Threading::ThreadState> : AK::Formatter<FormatString> { 111 ErrorOr<void> format(FormatBuilder& builder, Threading::ThreadState state) 112 { 113 DeprecatedString name = ""; 114 switch (state) { 115 case Threading::ThreadState::Detached: 116 name = "Detached"; 117 break; 118 case Threading::ThreadState::DetachedExited: 119 name = "DetachedExited"; 120 break; 121 case Threading::ThreadState::Exited: 122 name = "Exited"; 123 break; 124 case Threading::ThreadState::Joined: 125 name = "Joined"; 126 break; 127 case Threading::ThreadState::Running: 128 name = "Running"; 129 break; 130 case Threading::ThreadState::Startable: 131 name = "Startable"; 132 break; 133 default: 134 VERIFY_NOT_REACHED(); 135 } 136 return Formatter<FormatString>::format(builder, "{}"sv, name); 137 } 138};