Thread.h 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. #pragma once
  8. #include <AK/Assertions.h>
  9. #include <AK/DeprecatedString.h>
  10. #include <AK/DistinctNumeric.h>
  11. #include <AK/Function.h>
  12. #include <AK/Result.h>
  13. #include <LibCore/Object.h>
  14. #include <pthread.h>
  15. namespace Threading {
  16. AK_TYPEDEF_DISTINCT_ORDERED_ID(intptr_t, ThreadError);
  17. // States of userspace threads are simplified over actual kernel states (and possibly POSIX states).
  18. // 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.
  19. enum class ThreadState : u8 {
  20. // Thread has been constructed but not started.
  21. // Transitions to Running via start().
  22. Startable,
  23. // Thread has been started, might be running, and can be joined.
  24. // 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.
  25. // Transitions to Detached via detach(), transitions to Exited when the thread finishes its action function.
  26. Running,
  27. // Thread has not been detached and exited, and has to still be joined.
  28. // Transitions to Joined via join().
  29. Exited,
  30. // Thread has been started but also detached, meaning it cannot be joined.
  31. // Transitions to DetachedExited when the thread finishes its action function.
  32. Detached,
  33. // Thread has exited but was detached, meaning it cannot be joined.
  34. DetachedExited,
  35. // Thread has exited and been joined.
  36. Joined,
  37. };
  38. class Thread final : public Core::Object {
  39. C_OBJECT(Thread);
  40. public:
  41. virtual ~Thread();
  42. ErrorOr<void> set_priority(int priority);
  43. ErrorOr<int> get_priority() const;
  44. // Only callable in the Startable state.
  45. void start();
  46. // Only callable in the Running state.
  47. void detach();
  48. // Only callable in the Running or Exited states.
  49. template<typename T = void>
  50. Result<T, ThreadError> join();
  51. DeprecatedString thread_name() const;
  52. pthread_t tid() const;
  53. ThreadState state() const;
  54. bool is_started() const;
  55. bool needs_to_be_joined() const;
  56. bool has_exited() const;
  57. private:
  58. explicit Thread(Function<intptr_t()> action, StringView thread_name = {});
  59. Function<intptr_t()> m_action;
  60. pthread_t m_tid { 0 };
  61. DeprecatedString m_thread_name;
  62. Atomic<ThreadState> m_state { ThreadState::Startable };
  63. };
  64. template<typename T>
  65. Result<T, ThreadError> Thread::join()
  66. {
  67. VERIFY(needs_to_be_joined());
  68. void* thread_return = nullptr;
  69. int rc = pthread_join(m_tid, &thread_return);
  70. if (rc != 0) {
  71. return ThreadError { rc };
  72. }
  73. // The other thread has now stopped running, so a TOCTOU bug is not possible.
  74. // (If you call join from two different threads, you're doing something *very* wrong anyways.)
  75. VERIFY(m_state == ThreadState::Exited);
  76. m_state = ThreadState::Joined;
  77. if constexpr (IsVoid<T>)
  78. return {};
  79. else
  80. return { static_cast<T>(thread_return) };
  81. }
  82. }
  83. template<>
  84. struct AK::Formatter<Threading::Thread> : AK::Formatter<FormatString> {
  85. ErrorOr<void> format(FormatBuilder& builder, Threading::Thread const& thread)
  86. {
  87. return Formatter<FormatString>::format(builder, "Thread \"{}\"({})"sv, thread.thread_name(), thread.tid());
  88. }
  89. };
  90. template<>
  91. struct AK::Formatter<Threading::ThreadState> : AK::Formatter<FormatString> {
  92. ErrorOr<void> format(FormatBuilder& builder, Threading::ThreadState state)
  93. {
  94. DeprecatedString name = "";
  95. switch (state) {
  96. case Threading::ThreadState::Detached:
  97. name = "Detached";
  98. break;
  99. case Threading::ThreadState::DetachedExited:
  100. name = "DetachedExited";
  101. break;
  102. case Threading::ThreadState::Exited:
  103. name = "Exited";
  104. break;
  105. case Threading::ThreadState::Joined:
  106. name = "Joined";
  107. break;
  108. case Threading::ThreadState::Running:
  109. name = "Running";
  110. break;
  111. case Threading::ThreadState::Startable:
  112. name = "Startable";
  113. break;
  114. default:
  115. VERIFY_NOT_REACHED();
  116. }
  117. return Formatter<FormatString>::format(builder, "{}"sv, name);
  118. }
  119. };