Thread.cpp 4.7 KB

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