ThreadBlockers.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. /*
  2. * Copyright (c) 2020, The SerenityOS developers.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <Kernel/FileSystem/FileDescription.h>
  27. #include <Kernel/Net/Socket.h>
  28. #include <Kernel/Process.h>
  29. #include <Kernel/Scheduler.h>
  30. #include <Kernel/Thread.h>
  31. namespace Kernel {
  32. bool Thread::Blocker::set_block_condition(Thread::BlockCondition& block_condition, void* data)
  33. {
  34. ASSERT(!m_block_condition);
  35. if (block_condition.add_blocker(*this, data)) {
  36. m_block_condition = &block_condition;
  37. m_block_data = data;
  38. return true;
  39. }
  40. return false;
  41. }
  42. Thread::Blocker::~Blocker()
  43. {
  44. ScopedSpinLock lock(m_lock);
  45. if (m_block_condition)
  46. m_block_condition->remove_blocker(*this, m_block_data);
  47. }
  48. void Thread::Blocker::begin_blocking(Badge<Thread>)
  49. {
  50. ScopedSpinLock lock(m_lock);
  51. ASSERT(!m_is_blocking);
  52. ASSERT(!m_blocked_thread);
  53. m_blocked_thread = Thread::current();
  54. m_is_blocking = true;
  55. }
  56. auto Thread::Blocker::end_blocking(Badge<Thread>, bool did_timeout) -> BlockResult
  57. {
  58. ScopedSpinLock lock(m_lock);
  59. // if m_is_blocking is false here, some thread forced to
  60. // unblock us when we get here. This is only called from the
  61. // thread that was blocked.
  62. ASSERT(Thread::current() == m_blocked_thread);
  63. m_is_blocking = false;
  64. m_blocked_thread = nullptr;
  65. was_unblocked(did_timeout);
  66. return block_result();
  67. }
  68. Thread::JoinBlocker::JoinBlocker(Thread& joinee, KResult& try_join_result, void*& joinee_exit_value)
  69. : m_joinee(joinee)
  70. , m_joinee_exit_value(joinee_exit_value)
  71. {
  72. {
  73. // We need to hold our lock to avoid a race where try_join succeeds
  74. // but the joinee is joining immediately
  75. ScopedSpinLock lock(m_lock);
  76. try_join_result = joinee.try_join([&]() {
  77. if (!set_block_condition(joinee.m_join_condition))
  78. m_should_block = false;
  79. });
  80. m_join_error = try_join_result.is_error();
  81. if (m_join_error)
  82. m_should_block = false;
  83. }
  84. }
  85. void Thread::JoinBlocker::not_blocking(bool timeout_in_past)
  86. {
  87. if (!m_should_block) {
  88. // set_block_condition returned false, so unblock was already called
  89. ASSERT(!timeout_in_past);
  90. return;
  91. }
  92. // If we should have blocked but got here it must have been that the
  93. // timeout was already in the past. So we need to ask the BlockCondition
  94. // to supply us the information. We cannot hold the lock as unblock
  95. // could be called by the BlockCondition at any time!
  96. ASSERT(timeout_in_past);
  97. m_joinee->m_join_condition.try_unblock(*this);
  98. }
  99. bool Thread::JoinBlocker::unblock(void* value, bool from_add_blocker)
  100. {
  101. {
  102. ScopedSpinLock lock(m_lock);
  103. if (m_did_unblock)
  104. return false;
  105. m_did_unblock = true;
  106. m_joinee_exit_value = value;
  107. do_set_interrupted_by_death();
  108. }
  109. if (!from_add_blocker)
  110. unblock_from_blocker();
  111. return true;
  112. }
  113. Thread::QueueBlocker::QueueBlocker(WaitQueue& wait_queue, const char* block_reason)
  114. : m_block_reason(block_reason)
  115. {
  116. if (!set_block_condition(wait_queue, Thread::current()))
  117. m_should_block = false;
  118. }
  119. Thread::QueueBlocker::~QueueBlocker()
  120. {
  121. }
  122. bool Thread::QueueBlocker::unblock()
  123. {
  124. {
  125. ScopedSpinLock lock(m_lock);
  126. if (m_did_unblock)
  127. return false;
  128. m_did_unblock = true;
  129. }
  130. unblock_from_blocker();
  131. return true;
  132. }
  133. Thread::FileDescriptionBlocker::FileDescriptionBlocker(FileDescription& description, BlockFlags flags, BlockFlags& unblocked_flags)
  134. : m_blocked_description(description)
  135. , m_flags(flags)
  136. , m_unblocked_flags(unblocked_flags)
  137. {
  138. m_unblocked_flags = BlockFlags::None;
  139. if (!set_block_condition(description.block_condition()))
  140. m_should_block = false;
  141. }
  142. bool Thread::FileDescriptionBlocker::unblock(bool from_add_blocker, void*)
  143. {
  144. auto unblock_flags = m_blocked_description->should_unblock(m_flags);
  145. if (unblock_flags == BlockFlags::None)
  146. return false;
  147. {
  148. ScopedSpinLock lock(m_lock);
  149. if (m_did_unblock)
  150. return false;
  151. m_did_unblock = true;
  152. m_unblocked_flags = unblock_flags;
  153. }
  154. if (!from_add_blocker)
  155. unblock_from_blocker();
  156. return true;
  157. }
  158. void Thread::FileDescriptionBlocker::not_blocking(bool timeout_in_past)
  159. {
  160. if (!m_should_block) {
  161. // set_block_condition returned false, so unblock was already called
  162. ASSERT(!timeout_in_past);
  163. return;
  164. }
  165. // If we should have blocked but got here it must have been that the
  166. // timeout was already in the past. So we need to ask the BlockCondition
  167. // to supply us the information. We cannot hold the lock as unblock
  168. // could be called by the BlockCondition at any time!
  169. ASSERT(timeout_in_past);
  170. // Just call unblock here because we will query the file description
  171. // for the data and don't need any input from the FileBlockCondition.
  172. // However, it's possible that if timeout_in_past is true then FileBlockCondition
  173. // may call us at any given time, so our call to unblock here may fail.
  174. // Either way, unblock will be called at least once, which provides
  175. // all the data we need.
  176. unblock(false, nullptr);
  177. }
  178. const FileDescription& Thread::FileDescriptionBlocker::blocked_description() const
  179. {
  180. return m_blocked_description;
  181. }
  182. Thread::AcceptBlocker::AcceptBlocker(FileDescription& description, BlockFlags& unblocked_flags)
  183. : FileDescriptionBlocker(description, (BlockFlags)((u32)BlockFlags::Accept | (u32)BlockFlags::Exception), unblocked_flags)
  184. {
  185. }
  186. Thread::ConnectBlocker::ConnectBlocker(FileDescription& description, BlockFlags& unblocked_flags)
  187. : FileDescriptionBlocker(description, (BlockFlags)((u32)BlockFlags::Connect | (u32)BlockFlags::Exception), unblocked_flags)
  188. {
  189. }
  190. Thread::WriteBlocker::WriteBlocker(FileDescription& description, BlockFlags& unblocked_flags)
  191. : FileDescriptionBlocker(description, (BlockFlags)((u32)BlockFlags::Write | (u32)BlockFlags::Exception), unblocked_flags)
  192. {
  193. }
  194. auto Thread::WriteBlocker::override_timeout(const BlockTimeout& timeout) -> const BlockTimeout&
  195. {
  196. auto& description = blocked_description();
  197. if (description.is_socket()) {
  198. auto& socket = *description.socket();
  199. if (socket.has_send_timeout()) {
  200. m_timeout = BlockTimeout(false, &socket.send_timeout(), timeout.start_time(), timeout.clock_id());
  201. if (timeout.is_infinite() || (!m_timeout.is_infinite() && m_timeout.absolute_time() < timeout.absolute_time()))
  202. return m_timeout;
  203. }
  204. }
  205. return timeout;
  206. }
  207. Thread::ReadBlocker::ReadBlocker(FileDescription& description, BlockFlags& unblocked_flags)
  208. : FileDescriptionBlocker(description, (BlockFlags)((u32)BlockFlags::Read | (u32)BlockFlags::Exception), unblocked_flags)
  209. {
  210. }
  211. auto Thread::ReadBlocker::override_timeout(const BlockTimeout& timeout) -> const BlockTimeout&
  212. {
  213. auto& description = blocked_description();
  214. if (description.is_socket()) {
  215. auto& socket = *description.socket();
  216. if (socket.has_receive_timeout()) {
  217. m_timeout = BlockTimeout(false, &socket.receive_timeout(), timeout.start_time(), timeout.clock_id());
  218. if (timeout.is_infinite() || (!m_timeout.is_infinite() && m_timeout.absolute_time() < timeout.absolute_time()))
  219. return m_timeout;
  220. }
  221. }
  222. return timeout;
  223. }
  224. Thread::SleepBlocker::SleepBlocker(const BlockTimeout& deadline, timespec* remaining)
  225. : m_deadline(deadline)
  226. , m_remaining(remaining)
  227. {
  228. }
  229. auto Thread::SleepBlocker::override_timeout(const BlockTimeout& timeout) -> const BlockTimeout&
  230. {
  231. ASSERT(timeout.is_infinite()); // A timeout should not be provided
  232. // To simplify things only use the sleep deadline.
  233. return m_deadline;
  234. }
  235. void Thread::SleepBlocker::not_blocking(bool timeout_in_past)
  236. {
  237. // SleepBlocker::should_block should always return true, so timeout
  238. // in the past is the only valid case when this function is called
  239. ASSERT(timeout_in_past);
  240. calculate_remaining();
  241. }
  242. void Thread::SleepBlocker::was_unblocked(bool did_timeout)
  243. {
  244. Blocker::was_unblocked(did_timeout);
  245. calculate_remaining();
  246. }
  247. void Thread::SleepBlocker::calculate_remaining()
  248. {
  249. if (!m_remaining)
  250. return;
  251. auto time_now = TimeManagement::the().current_time(m_deadline.clock_id()).value();
  252. if (time_now < m_deadline.absolute_time())
  253. timespec_sub(m_deadline.absolute_time(), time_now, *m_remaining);
  254. else
  255. *m_remaining = {};
  256. }
  257. Thread::BlockResult Thread::SleepBlocker::block_result()
  258. {
  259. auto result = Blocker::block_result();
  260. if (result == Thread::BlockResult::InterruptedByTimeout)
  261. return Thread::BlockResult::WokeNormally;
  262. return result;
  263. }
  264. Thread::SelectBlocker::SelectBlocker(FDVector& fds)
  265. : m_fds(fds)
  266. {
  267. for (auto& fd_entry : m_fds) {
  268. fd_entry.unblocked_flags = FileBlocker::BlockFlags::None;
  269. if (!m_should_block)
  270. continue;
  271. if (!fd_entry.description->block_condition().add_blocker(*this, &fd_entry))
  272. m_should_block = false;
  273. }
  274. }
  275. Thread::SelectBlocker::~SelectBlocker()
  276. {
  277. for (auto& fd_entry : m_fds)
  278. fd_entry.description->block_condition().remove_blocker(*this, &fd_entry);
  279. }
  280. void Thread::SelectBlocker::not_blocking(bool timeout_in_past)
  281. {
  282. // Either the timeout was in the past or we didn't add all blockers
  283. ASSERT(timeout_in_past || !m_should_block);
  284. ScopedSpinLock lock(m_lock);
  285. if (!m_did_unblock) {
  286. m_did_unblock = true;
  287. if (!timeout_in_past) {
  288. auto count = collect_unblocked_flags();
  289. ASSERT(count > 0);
  290. }
  291. }
  292. }
  293. bool Thread::SelectBlocker::unblock(bool from_add_blocker, void* data)
  294. {
  295. ASSERT(data); // data is a pointer to an entry in the m_fds vector
  296. auto& fd_info = *static_cast<FDInfo*>(data);
  297. {
  298. ScopedSpinLock lock(m_lock);
  299. if (m_did_unblock)
  300. return false;
  301. auto unblock_flags = fd_info.description->should_unblock(fd_info.block_flags);
  302. if (unblock_flags == BlockFlags::None)
  303. return false;
  304. m_did_unblock = true;
  305. // We need to store unblock_flags here, otherwise someone else
  306. // affecting this file descriptor could change the information
  307. // between now and when was_unblocked is called!
  308. fd_info.unblocked_flags = unblock_flags;
  309. }
  310. // Only do this once for the first one
  311. if (!from_add_blocker)
  312. unblock_from_blocker();
  313. return true;
  314. }
  315. size_t Thread::SelectBlocker::collect_unblocked_flags()
  316. {
  317. size_t count = 0;
  318. for (auto& fd_entry : m_fds) {
  319. ASSERT(fd_entry.block_flags != FileBlocker::BlockFlags::None);
  320. // unblock will have set at least the first descriptor's unblock
  321. // flags that triggered the unblock. Make sure we don't discard that
  322. // information as it may have changed by now!
  323. if (fd_entry.unblocked_flags == FileBlocker::BlockFlags::None)
  324. fd_entry.unblocked_flags = fd_entry.description->should_unblock(fd_entry.block_flags);
  325. if (fd_entry.unblocked_flags != FileBlocker::BlockFlags::None)
  326. count++;
  327. }
  328. return count;
  329. }
  330. void Thread::SelectBlocker::was_unblocked(bool did_timeout)
  331. {
  332. Blocker::was_unblocked(did_timeout);
  333. if (!did_timeout && !was_interrupted()) {
  334. {
  335. ScopedSpinLock lock(m_lock);
  336. ASSERT(m_did_unblock);
  337. }
  338. size_t count = collect_unblocked_flags();
  339. // If we were blocked and didn't time out, we should have at least one unblocked fd!
  340. ASSERT(count > 0);
  341. }
  342. }
  343. void Thread::WaitBlockCondition::try_unblock(Thread::WaitBlocker& blocker)
  344. {
  345. ScopedSpinLock lock(m_lock);
  346. // We if we have any processes pending
  347. for (size_t i = 0; i < m_threads.size(); i++) {
  348. auto& info = m_threads[i];
  349. // We need to call unblock as if we were called from add_blocker
  350. // so that we don't trigger a context switch by yielding!
  351. if (info.was_waited && blocker.is_wait())
  352. continue; // This state was already waited on, do not unblock
  353. if (blocker.unblock(info.thread, info.flags, info.signal, true)) {
  354. if (blocker.is_wait()) {
  355. if (info.flags == Thread::WaitBlocker::UnblockFlags::Terminated)
  356. m_threads.remove(i);
  357. else
  358. info.was_waited = true;
  359. }
  360. break;
  361. }
  362. }
  363. }
  364. void Thread::WaitBlockCondition::disowned_by_waiter(Process& process)
  365. {
  366. ScopedSpinLock lock(m_lock);
  367. if (m_finalized)
  368. return;
  369. for (size_t i = 0; i < m_threads.size();) {
  370. auto& info = m_threads[i];
  371. if (&info.thread->process() == &process) {
  372. do_unblock([&](Blocker& b, void*) {
  373. ASSERT(b.blocker_type() == Blocker::Type::Wait);
  374. auto& blocker = static_cast<WaitBlocker&>(b);
  375. bool did_unblock = blocker.unblock(info.thread, WaitBlocker::UnblockFlags::Disowned, 0, false);
  376. ASSERT(did_unblock); // disowning must unblock everyone
  377. return true;
  378. });
  379. m_threads.remove(i);
  380. continue;
  381. }
  382. i++;
  383. }
  384. }
  385. bool Thread::WaitBlockCondition::unblock(Thread& thread, WaitBlocker::UnblockFlags flags, u8 signal)
  386. {
  387. ASSERT(flags != WaitBlocker::UnblockFlags::Disowned);
  388. bool did_unblock_any = false;
  389. bool did_wait = false;
  390. bool was_waited_already = false;
  391. ScopedSpinLock lock(m_lock);
  392. if (m_finalized)
  393. return false;
  394. if (flags != WaitBlocker::UnblockFlags::Terminated) {
  395. // First check if this state was already waited on
  396. for (auto& info : m_threads) {
  397. if (info.thread == &thread) {
  398. was_waited_already = info.was_waited;
  399. break;
  400. }
  401. }
  402. }
  403. do_unblock([&](Blocker& b, void*) {
  404. ASSERT(b.blocker_type() == Blocker::Type::Wait);
  405. auto& blocker = static_cast<WaitBlocker&>(b);
  406. if (was_waited_already && blocker.is_wait())
  407. return false; // This state was already waited on, do not unblock
  408. if (blocker.unblock(thread, flags, signal, false)) {
  409. did_wait |= blocker.is_wait(); // anyone requesting a wait
  410. did_unblock_any = true;
  411. return true;
  412. }
  413. return false;
  414. });
  415. // If no one has waited (yet), or this wasn't a wait, or if it's anything other than
  416. // UnblockFlags::Terminated then add it to your list
  417. if (!did_unblock_any || !did_wait || flags != WaitBlocker::UnblockFlags::Terminated) {
  418. bool updated_existing = false;
  419. for (auto& info : m_threads) {
  420. if (info.thread == &thread) {
  421. ASSERT(info.flags != WaitBlocker::UnblockFlags::Terminated);
  422. info.flags = flags;
  423. info.signal = signal;
  424. info.was_waited = did_wait;
  425. updated_existing = true;
  426. break;
  427. }
  428. }
  429. if (!updated_existing)
  430. m_threads.append(ThreadBlockInfo(thread, flags, signal));
  431. }
  432. return did_unblock_any;
  433. }
  434. bool Thread::WaitBlockCondition::should_add_blocker(Blocker& b, void*)
  435. {
  436. // NOTE: m_lock is held already!
  437. if (m_finalized)
  438. return false;
  439. ASSERT(b.blocker_type() == Blocker::Type::Wait);
  440. auto& blocker = static_cast<WaitBlocker&>(b);
  441. // See if we can match any process immediately
  442. for (size_t i = 0; i < m_threads.size(); i++) {
  443. auto& info = m_threads[i];
  444. if (blocker.unblock(info.thread, info.flags, info.signal, true)) {
  445. // Only remove the entry if UnblockFlags::Terminated
  446. if (info.flags == Thread::WaitBlocker::UnblockFlags::Terminated && blocker.is_wait())
  447. m_threads.remove(i);
  448. return false;
  449. }
  450. }
  451. return true;
  452. }
  453. void Thread::WaitBlockCondition::finalize()
  454. {
  455. ScopedSpinLock lock(m_lock);
  456. ASSERT(!m_finalized);
  457. m_finalized = true;
  458. // Clear the list of threads here so we can drop the references to them
  459. m_threads.clear();
  460. // No more waiters, drop the last reference immediately. This may
  461. // cause us to be destructed ourselves!
  462. ASSERT(m_process.ref_count() > 0);
  463. m_process.unref();
  464. }
  465. Thread::WaitBlocker::WaitBlocker(int wait_options, idtype_t id_type, pid_t id, KResultOr<siginfo_t>& result)
  466. : m_wait_options(wait_options)
  467. , m_id_type(id_type)
  468. , m_waitee_id(id)
  469. , m_result(result)
  470. , m_should_block(!(m_wait_options & WNOHANG))
  471. {
  472. switch (id_type) {
  473. case P_PID: {
  474. m_waitee = Process::from_pid(m_waitee_id);
  475. if (!m_waitee || m_waitee->ppid() != Process::current()->pid()) {
  476. m_result = KResult(-ECHILD);
  477. m_error = true;
  478. return;
  479. }
  480. break;
  481. }
  482. case P_PGID: {
  483. m_waitee_group = ProcessGroup::from_pgid(m_waitee_id);
  484. if (!m_waitee_group) {
  485. m_result = KResult(-ECHILD);
  486. m_error = true;
  487. return;
  488. }
  489. break;
  490. }
  491. case P_ALL:
  492. break;
  493. default:
  494. ASSERT_NOT_REACHED();
  495. }
  496. // NOTE: unblock may be called within set_block_condition, in which
  497. // case it means that we already have a match without having to block.
  498. // In that case set_block_condition will return false.
  499. if (m_error || !set_block_condition(Process::current()->wait_block_condition()))
  500. m_should_block = false;
  501. }
  502. void Thread::WaitBlocker::not_blocking(bool timeout_in_past)
  503. {
  504. ASSERT(timeout_in_past || !m_should_block);
  505. if (!m_error)
  506. Process::current()->wait_block_condition().try_unblock(*this);
  507. }
  508. void Thread::WaitBlocker::was_unblocked(bool)
  509. {
  510. bool got_sigchld, try_unblock;
  511. {
  512. ScopedSpinLock lock(m_lock);
  513. try_unblock = !m_did_unblock;
  514. got_sigchld = m_got_sigchild;
  515. }
  516. if (try_unblock)
  517. Process::current()->wait_block_condition().try_unblock(*this);
  518. // If we were interrupted by SIGCHLD (which gets special handling
  519. // here) we're not going to return with EINTR. But we're going to
  520. // deliver SIGCHLD (only) here.
  521. auto* current_thread = Thread::current();
  522. if (got_sigchld && current_thread->state() != State::Stopped)
  523. current_thread->try_dispatch_one_pending_signal(SIGCHLD);
  524. }
  525. void Thread::WaitBlocker::do_was_disowned()
  526. {
  527. ASSERT(!m_did_unblock);
  528. m_did_unblock = true;
  529. m_result = KResult(-ECHILD);
  530. }
  531. void Thread::WaitBlocker::do_set_result(const siginfo_t& result)
  532. {
  533. ASSERT(!m_did_unblock);
  534. m_did_unblock = true;
  535. m_result = result;
  536. if (do_get_interrupted_by_signal() == SIGCHLD) {
  537. // This makes it so that wait() will return normally despite the
  538. // fact that SIGCHLD was delivered. Calling do_clear_interrupted_by_signal
  539. // will disable dispatching signals in Thread::block and prevent
  540. // it from returning with EINTR. We will then manually dispatch
  541. // SIGCHLD (and only SIGCHLD) in was_unblocked.
  542. m_got_sigchild = true;
  543. do_clear_interrupted_by_signal();
  544. }
  545. }
  546. bool Thread::WaitBlocker::unblock(Thread& thread, UnblockFlags flags, u8 signal, bool from_add_blocker)
  547. {
  548. ASSERT(flags != UnblockFlags::Terminated || signal == 0); // signal argument should be ignored for Terminated
  549. auto& process = thread.process();
  550. switch (m_id_type) {
  551. case P_PID:
  552. ASSERT(m_waitee);
  553. if (process.pid() != m_waitee_id && thread.tid() != m_waitee_id) // TODO: pid/tid
  554. return false;
  555. break;
  556. case P_PGID:
  557. ASSERT(m_waitee_group);
  558. if (process.pgid() != m_waitee_group->pgid())
  559. return false;
  560. break;
  561. case P_ALL:
  562. if (flags == UnblockFlags::Disowned) {
  563. // Generic waiter won't be unblocked by disown
  564. return false;
  565. }
  566. break;
  567. default:
  568. ASSERT_NOT_REACHED();
  569. }
  570. switch (flags) {
  571. case UnblockFlags::Terminated:
  572. if (!(m_wait_options & WEXITED))
  573. return false;
  574. break;
  575. case UnblockFlags::Stopped:
  576. if (!(m_wait_options & WSTOPPED))
  577. return false;
  578. if (!(m_wait_options & WUNTRACED) && !thread.is_traced())
  579. return false;
  580. break;
  581. case UnblockFlags::Continued:
  582. if (!(m_wait_options & WCONTINUED))
  583. return false;
  584. if (!(m_wait_options & WUNTRACED) && !thread.is_traced())
  585. return false;
  586. break;
  587. case UnblockFlags::Disowned:
  588. ScopedSpinLock lock(m_lock);
  589. // Disowning must unblock anyone waiting for this process explicitly
  590. if (!m_did_unblock)
  591. do_was_disowned();
  592. return true;
  593. }
  594. if (flags == UnblockFlags::Terminated) {
  595. ASSERT(process.is_dead());
  596. ScopedSpinLock lock(m_lock);
  597. if (m_did_unblock)
  598. return false;
  599. // Up until this point, this function may have been called
  600. // more than once!
  601. do_set_result(process.wait_info());
  602. } else {
  603. siginfo_t siginfo;
  604. memset(&siginfo, 0, sizeof(siginfo));
  605. {
  606. ScopedSpinLock lock(g_scheduler_lock);
  607. // We need to gather the information before we release the sheduler lock!
  608. siginfo.si_signo = SIGCHLD;
  609. siginfo.si_pid = thread.tid().value();
  610. siginfo.si_uid = process.uid();
  611. siginfo.si_status = signal;
  612. switch (flags) {
  613. case UnblockFlags::Terminated:
  614. case UnblockFlags::Disowned:
  615. ASSERT_NOT_REACHED();
  616. case UnblockFlags::Stopped:
  617. siginfo.si_code = CLD_STOPPED;
  618. break;
  619. case UnblockFlags::Continued:
  620. siginfo.si_code = CLD_CONTINUED;
  621. break;
  622. }
  623. }
  624. ScopedSpinLock lock(m_lock);
  625. if (m_did_unblock)
  626. return false;
  627. // Up until this point, this function may have been called
  628. // more than once!
  629. do_set_result(siginfo);
  630. }
  631. if (!from_add_blocker) {
  632. // Only call unblock if we weren't called from within set_block_condition!
  633. ASSERT(flags != UnblockFlags::Disowned);
  634. unblock_from_blocker();
  635. }
  636. // Because this may be called from add_blocker, in which case we should
  637. // not be actually trying to unblock the thread (because it hasn't actually
  638. // been blocked yet), we need to return true anyway
  639. return true;
  640. }
  641. }