Thread.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  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. : m_action(move(action))
  13. , m_thread_name(thread_name.is_null() ? ""sv : thread_name)
  14. {
  15. }
  16. Thread::~Thread()
  17. {
  18. if (needs_to_be_joined()) {
  19. dbgln("Destroying {} while it is still running undetached!", *this);
  20. [[maybe_unused]] auto res = join();
  21. }
  22. }
  23. ErrorOr<void> Thread::set_priority(int priority)
  24. {
  25. // MacOS has an extra __opaque field, so list initialization will not compile on MacOS Lagom.
  26. sched_param scheduling_parameters {};
  27. scheduling_parameters.sched_priority = priority;
  28. int result = pthread_setschedparam(m_tid, 0, &scheduling_parameters);
  29. if (result != 0)
  30. return Error::from_errno(result);
  31. return {};
  32. }
  33. ErrorOr<int> Thread::get_priority() const
  34. {
  35. sched_param scheduling_parameters {};
  36. int policy;
  37. int result = pthread_getschedparam(m_tid, &policy, &scheduling_parameters);
  38. if (result != 0)
  39. return Error::from_errno(result);
  40. return scheduling_parameters.sched_priority;
  41. }
  42. ByteString Thread::thread_name() const { return m_thread_name; }
  43. pthread_t Thread::tid() const { return m_tid; }
  44. ThreadState Thread::state() const { return m_state; }
  45. bool Thread::is_started() const { return m_state != ThreadState::Startable; }
  46. bool Threading::Thread::needs_to_be_joined() const
  47. {
  48. auto state = m_state.load();
  49. return state == ThreadState::Running || state == ThreadState::Exited;
  50. }
  51. bool Threading::Thread::has_exited() const
  52. {
  53. auto state = m_state.load();
  54. return state == ThreadState::Joined || state == ThreadState::Exited || state == ThreadState::DetachedExited;
  55. }
  56. void Thread::start()
  57. {
  58. VERIFY(!is_started());
  59. // Set this first so that the other thread starts out seeing m_state == Running.
  60. m_state = Threading::ThreadState::Running;
  61. int rc = pthread_create(
  62. &m_tid,
  63. // FIXME: Use pthread_attr_t to start a thread detached if that was requested by the user before the call to start().
  64. nullptr,
  65. [](void* arg) -> void* {
  66. auto self = adopt_ref(*static_cast<Thread*>(arg));
  67. auto exit_code = self->m_action();
  68. auto expected = Threading::ThreadState::Running;
  69. // This code might race with a call to detach().
  70. if (!self->m_state.compare_exchange_strong(expected, Threading::ThreadState::Exited)) {
  71. // If the original state was Detached, we need to set to DetachedExited instead.
  72. if (expected == Threading::ThreadState::Detached) {
  73. if (!self->m_state.compare_exchange_strong(expected, Threading::ThreadState::DetachedExited)) {
  74. dbgln("Thread logic bug: Found thread state {} while trying to set ExitedDetached state!", expected);
  75. VERIFY_NOT_REACHED();
  76. }
  77. } else {
  78. dbgln("Thread logic bug: Found thread state {} while trying to set Exited state!", expected);
  79. VERIFY_NOT_REACHED();
  80. }
  81. }
  82. return reinterpret_cast<void*>(exit_code);
  83. },
  84. &NonnullRefPtr(*this).leak_ref());
  85. VERIFY(rc == 0);
  86. #ifdef AK_OS_SERENITY
  87. if (!m_thread_name.is_empty()) {
  88. rc = pthread_setname_np(m_tid, m_thread_name.characters());
  89. VERIFY(rc == 0);
  90. }
  91. #endif
  92. dbgln("Started {}", *this);
  93. }
  94. void Thread::detach()
  95. {
  96. auto expected = Threading::ThreadState::Running;
  97. // This code might race with the other thread exiting.
  98. if (!m_state.compare_exchange_strong(expected, Threading::ThreadState::Detached)) {
  99. if (expected == Threading::ThreadState::Exited)
  100. return;
  101. // Always report a precise error before crashing. These kinds of bugs are hard to reproduce.
  102. dbgln("Thread logic bug: trying to detach {} in state {}, which is neither Started nor Exited", this, expected);
  103. VERIFY_NOT_REACHED();
  104. }
  105. int rc = pthread_detach(m_tid);
  106. VERIFY(rc == 0);
  107. }
  108. }