Serenity Operating System
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};