Serenity Operating System
at master 858 lines 28 kB view raw
1/* 2 * Copyright (c) 2020, the SerenityOS developers. 3 * Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/BuiltinWrappers.h> 9#include <Kernel/Debug.h> 10#include <Kernel/FileSystem/OpenFileDescription.h> 11#include <Kernel/Net/Socket.h> 12#include <Kernel/Process.h> 13#include <Kernel/Scheduler.h> 14#include <Kernel/Thread.h> 15 16namespace Kernel { 17 18Thread::BlockTimeout::BlockTimeout(bool is_absolute, Time const* time, Time const* start_time, clockid_t clock_id) 19 : m_clock_id(clock_id) 20 , m_infinite(!time) 21{ 22 if (m_infinite) 23 return; 24 if (*time > Time::zero()) 25 m_time = *time; 26 m_start_time = start_time ? *start_time : TimeManagement::the().current_time(clock_id); 27 if (!is_absolute) 28 m_time += m_start_time; 29} 30 31bool Thread::Blocker::add_to_blocker_set(Thread::BlockerSet& blocker_set, void* data) 32{ 33 VERIFY(!m_blocker_set); 34 if (blocker_set.add_blocker(*this, data)) { 35 m_blocker_set = &blocker_set; 36 return true; 37 } 38 return false; 39} 40 41Thread::Blocker::~Blocker() = default; 42 43void Thread::Blocker::finalize() 44{ 45 if (m_blocker_set) 46 m_blocker_set->remove_blocker(*this); 47} 48 49bool Thread::Blocker::setup_blocker() 50{ 51 return true; 52} 53 54void Thread::Blocker::begin_blocking(Badge<Thread>) 55{ 56 SpinlockLocker lock(m_lock); 57 VERIFY(!m_is_blocking); 58 m_is_blocking = true; 59} 60 61auto Thread::Blocker::end_blocking(Badge<Thread>, bool did_timeout) -> BlockResult 62{ 63 SpinlockLocker lock(m_lock); 64 // if m_is_blocking is false here, some thread forced to 65 // unblock us when we get here. This is only called from the 66 // thread that was blocked. 67 VERIFY(Thread::current() == m_thread); 68 m_is_blocking = false; 69 70 was_unblocked(did_timeout); 71 return block_result(); 72} 73 74Thread::JoinBlocker::JoinBlocker(Thread& joinee, ErrorOr<void>& try_join_result, void*& joinee_exit_value) 75 : m_joinee(joinee) 76 , m_joinee_exit_value(joinee_exit_value) 77 , m_try_join_result(try_join_result) 78{ 79} 80 81bool Thread::JoinBlocker::setup_blocker() 82{ 83 // We need to hold our lock to avoid a race where try_join succeeds 84 // but the joinee is joining immediately 85 SpinlockLocker lock(m_lock); 86 bool should_block = true; 87 m_try_join_result = m_joinee->try_join([&]() { 88 if (!add_to_blocker_set(m_joinee->m_join_blocker_set)) 89 should_block = false; 90 }); 91 if (m_try_join_result.is_error()) 92 return false; 93 return should_block; 94} 95 96void Thread::JoinBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason) 97{ 98 // If we should have blocked but got here it must have been that the 99 // timeout was already in the past. So we need to ask the BlockerSet 100 // to supply us the information. We cannot hold the lock as unblock 101 // could be called by the BlockerSet at any time! 102 if (reason == UnblockImmediatelyReason::TimeoutInThePast) { 103 m_joinee->m_join_blocker_set.try_unblock(*this); 104 } 105} 106 107bool Thread::JoinBlocker::unblock(void* value, bool from_add_blocker) 108{ 109 { 110 SpinlockLocker lock(m_lock); 111 if (m_did_unblock) 112 return false; 113 m_did_unblock = true; 114 m_joinee_exit_value = value; 115 do_set_interrupted_by_death(); 116 } 117 118 if (!from_add_blocker) 119 unblock_from_blocker(); 120 return true; 121} 122 123Thread::WaitQueueBlocker::WaitQueueBlocker(WaitQueue& wait_queue, StringView block_reason) 124 : m_wait_queue(wait_queue) 125 , m_block_reason(block_reason) 126{ 127} 128 129bool Thread::WaitQueueBlocker::setup_blocker() 130{ 131 return add_to_blocker_set(m_wait_queue); 132} 133 134Thread::WaitQueueBlocker::~WaitQueueBlocker() = default; 135 136bool Thread::WaitQueueBlocker::unblock() 137{ 138 { 139 SpinlockLocker lock(m_lock); 140 if (m_did_unblock) 141 return false; 142 m_did_unblock = true; 143 } 144 145 unblock_from_blocker(); 146 return true; 147} 148 149Thread::FutexBlocker::FutexBlocker(FutexQueue& futex_queue, u32 bitset) 150 : m_futex_queue(futex_queue) 151 , m_bitset(bitset) 152{ 153} 154 155bool Thread::FutexBlocker::setup_blocker() 156{ 157 return add_to_blocker_set(m_futex_queue); 158} 159 160Thread::FutexBlocker::~FutexBlocker() = default; 161 162void Thread::FutexBlocker::finish_requeue(FutexQueue& futex_queue) 163{ 164 VERIFY(m_lock.is_locked_by_current_processor()); 165 set_blocker_set_raw_locked(&futex_queue); 166 // We can now release the lock 167 m_lock.unlock(m_previous_interrupts_state); 168} 169 170bool Thread::FutexBlocker::unblock_bitset(u32 bitset) 171{ 172 { 173 SpinlockLocker lock(m_lock); 174 if (m_did_unblock || (bitset != FUTEX_BITSET_MATCH_ANY && (m_bitset & bitset) == 0)) 175 return false; 176 177 m_did_unblock = true; 178 } 179 180 unblock_from_blocker(); 181 return true; 182} 183 184bool Thread::FutexBlocker::unblock(bool force) 185{ 186 { 187 SpinlockLocker lock(m_lock); 188 if (m_did_unblock) 189 return force; 190 m_did_unblock = true; 191 } 192 193 unblock_from_blocker(); 194 return true; 195} 196 197Thread::OpenFileDescriptionBlocker::OpenFileDescriptionBlocker(OpenFileDescription& description, BlockFlags flags, BlockFlags& unblocked_flags) 198 : m_blocked_description(description) 199 , m_flags(flags) 200 , m_unblocked_flags(unblocked_flags) 201{ 202} 203 204bool Thread::OpenFileDescriptionBlocker::setup_blocker() 205{ 206 m_unblocked_flags = BlockFlags::None; 207 return add_to_blocker_set(m_blocked_description->blocker_set()); 208} 209 210bool Thread::OpenFileDescriptionBlocker::unblock_if_conditions_are_met(bool from_add_blocker, void*) 211{ 212 auto unblock_flags = m_blocked_description->should_unblock(m_flags); 213 if (unblock_flags == BlockFlags::None) 214 return false; 215 216 { 217 SpinlockLocker lock(m_lock); 218 if (m_did_unblock) 219 return false; 220 m_did_unblock = true; 221 m_unblocked_flags = unblock_flags; 222 } 223 224 if (!from_add_blocker) 225 unblock_from_blocker(); 226 return true; 227} 228 229void Thread::OpenFileDescriptionBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason) 230{ 231 if (reason == UnblockImmediatelyReason::UnblockConditionAlreadyMet) 232 return; 233 234 // If we should have blocked but got here it must have been that the 235 // timeout was already in the past. So we need to ask the BlockerSet 236 // to supply us the information. We cannot hold the lock as unblock 237 // could be called by the BlockerSet at any time! 238 VERIFY(reason == UnblockImmediatelyReason::TimeoutInThePast); 239 240 // Just call unblock_if_conditions_are_met here because we will query the file description 241 // for the data and don't need any input from the FileBlockerSet. 242 // However, it's possible that if timeout_in_past is true then FileBlockerSet 243 // may call us at any given time, so our call to unblock here may fail. 244 // Either way, unblock will be called at least once, which provides 245 // all the data we need. 246 unblock_if_conditions_are_met(false, nullptr); 247} 248 249OpenFileDescription const& Thread::OpenFileDescriptionBlocker::blocked_description() const 250{ 251 return m_blocked_description; 252} 253 254Thread::AcceptBlocker::AcceptBlocker(OpenFileDescription& description, BlockFlags& unblocked_flags) 255 : OpenFileDescriptionBlocker(description, BlockFlags::Accept | BlockFlags::Exception, unblocked_flags) 256{ 257} 258 259Thread::ConnectBlocker::ConnectBlocker(OpenFileDescription& description, BlockFlags& unblocked_flags) 260 : OpenFileDescriptionBlocker(description, BlockFlags::Connect | BlockFlags::Exception, unblocked_flags) 261{ 262} 263 264Thread::WriteBlocker::WriteBlocker(OpenFileDescription& description, BlockFlags& unblocked_flags) 265 : OpenFileDescriptionBlocker(description, BlockFlags::Write | BlockFlags::Exception, unblocked_flags) 266{ 267} 268 269auto Thread::WriteBlocker::override_timeout(BlockTimeout const& timeout) -> BlockTimeout const& 270{ 271 auto const& description = blocked_description(); 272 if (description.is_socket()) { 273 auto const& socket = *description.socket(); 274 if (socket.has_send_timeout()) { 275 Time send_timeout = socket.send_timeout(); 276 m_timeout = BlockTimeout(false, &send_timeout, timeout.start_time(), timeout.clock_id()); 277 if (timeout.is_infinite() || (!m_timeout.is_infinite() && m_timeout.absolute_time() < timeout.absolute_time())) 278 return m_timeout; 279 } 280 } 281 return timeout; 282} 283 284Thread::ReadBlocker::ReadBlocker(OpenFileDescription& description, BlockFlags& unblocked_flags) 285 : OpenFileDescriptionBlocker(description, BlockFlags::Read | BlockFlags::Exception, unblocked_flags) 286{ 287} 288 289auto Thread::ReadBlocker::override_timeout(BlockTimeout const& timeout) -> BlockTimeout const& 290{ 291 auto const& description = blocked_description(); 292 if (description.is_socket()) { 293 auto const& socket = *description.socket(); 294 if (socket.has_receive_timeout()) { 295 Time receive_timeout = socket.receive_timeout(); 296 m_timeout = BlockTimeout(false, &receive_timeout, timeout.start_time(), timeout.clock_id()); 297 if (timeout.is_infinite() || (!m_timeout.is_infinite() && m_timeout.absolute_time() < timeout.absolute_time())) 298 return m_timeout; 299 } 300 } 301 return timeout; 302} 303 304Thread::SleepBlocker::SleepBlocker(BlockTimeout const& deadline, Time* remaining) 305 : m_deadline(deadline) 306 , m_remaining(remaining) 307{ 308} 309 310auto Thread::SleepBlocker::override_timeout(BlockTimeout const& timeout) -> BlockTimeout const& 311{ 312 VERIFY(timeout.is_infinite()); // A timeout should not be provided 313 // To simplify things only use the sleep deadline. 314 return m_deadline; 315} 316 317void Thread::SleepBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason) 318{ 319 // SleepBlocker::should_block should always return true, so timeout 320 // in the past is the only valid case when this function is called 321 VERIFY(reason == UnblockImmediatelyReason::TimeoutInThePast); 322 calculate_remaining(); 323} 324 325void Thread::SleepBlocker::was_unblocked(bool did_timeout) 326{ 327 Blocker::was_unblocked(did_timeout); 328 329 calculate_remaining(); 330} 331 332void Thread::SleepBlocker::calculate_remaining() 333{ 334 if (!m_remaining) 335 return; 336 auto time_now = TimeManagement::the().current_time(m_deadline.clock_id()); 337 if (time_now < m_deadline.absolute_time()) 338 *m_remaining = m_deadline.absolute_time() - time_now; 339 else 340 *m_remaining = {}; 341} 342 343Thread::BlockResult Thread::SleepBlocker::block_result() 344{ 345 auto result = Blocker::block_result(); 346 if (result == Thread::BlockResult::InterruptedByTimeout) 347 return Thread::BlockResult::WokeNormally; 348 return result; 349} 350 351Thread::SelectBlocker::SelectBlocker(FDVector& fds) 352 : m_fds(fds) 353{ 354} 355 356bool Thread::SelectBlocker::setup_blocker() 357{ 358 bool should_block = true; 359 for (auto& fd_entry : m_fds) { 360 fd_entry.unblocked_flags = FileBlocker::BlockFlags::None; 361 362 if (!should_block) 363 continue; 364 if (!fd_entry.description) { 365 should_block = false; 366 continue; 367 } 368 if (!fd_entry.description->blocker_set().add_blocker(*this, &fd_entry)) 369 should_block = false; 370 } 371 return should_block; 372} 373 374Thread::SelectBlocker::~SelectBlocker() = default; 375 376void Thread::SelectBlocker::finalize() 377{ 378 Thread::FileBlocker::finalize(); 379 for (auto& fd_entry : m_fds) { 380 if (fd_entry.description) 381 fd_entry.description->blocker_set().remove_blocker(*this); 382 } 383} 384 385void Thread::SelectBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason) 386{ 387 SpinlockLocker lock(m_lock); 388 if (m_did_unblock) 389 return; 390 m_did_unblock = true; 391 if (reason == UnblockImmediatelyReason::UnblockConditionAlreadyMet) { 392 auto count = collect_unblocked_flags(); 393 VERIFY(count > 0); 394 } 395} 396 397bool Thread::SelectBlocker::unblock_if_conditions_are_met(bool from_add_blocker, void* data) 398{ 399 VERIFY(data); // data is a pointer to an entry in the m_fds vector 400 auto& fd_info = *static_cast<FDInfo*>(data); 401 402 { 403 SpinlockLocker lock(m_lock); 404 if (m_did_unblock) 405 return false; 406 407 VERIFY(fd_info.description); 408 auto unblock_flags = fd_info.description->should_unblock(fd_info.block_flags); 409 if (unblock_flags == BlockFlags::None) 410 return false; 411 412 m_did_unblock = true; 413 414 // We need to store unblock_flags here, otherwise someone else 415 // affecting this file descriptor could change the information 416 // between now and when was_unblocked is called! 417 fd_info.unblocked_flags = unblock_flags; 418 } 419 420 // Only do this once for the first one 421 if (!from_add_blocker) 422 unblock_from_blocker(); 423 return true; 424} 425 426size_t Thread::SelectBlocker::collect_unblocked_flags() 427{ 428 size_t count = 0; 429 for (auto& fd_entry : m_fds) { 430 VERIFY(fd_entry.block_flags != FileBlocker::BlockFlags::None); 431 432 if (!fd_entry.description) { 433 count++; 434 continue; 435 } 436 437 // unblock will have set at least the first descriptor's unblock 438 // flags that triggered the unblock. Make sure we don't discard that 439 // information as it may have changed by now! 440 if (fd_entry.unblocked_flags == FileBlocker::BlockFlags::None) 441 fd_entry.unblocked_flags = fd_entry.description->should_unblock(fd_entry.block_flags); 442 443 if (fd_entry.unblocked_flags != FileBlocker::BlockFlags::None) 444 count++; 445 } 446 return count; 447} 448 449void Thread::SelectBlocker::was_unblocked(bool did_timeout) 450{ 451 Blocker::was_unblocked(did_timeout); 452 if (!did_timeout && !was_interrupted()) { 453 { 454 SpinlockLocker lock(m_lock); 455 VERIFY(m_did_unblock); 456 } 457 size_t count = collect_unblocked_flags(); 458 // If we were blocked and didn't time out, we should have at least one unblocked fd! 459 VERIFY(count > 0); 460 } 461} 462 463Thread::SignalBlocker::SignalBlocker(sigset_t pending_set, siginfo_t& result) 464 : m_pending_set(pending_set) 465 , m_result(result) 466{ 467} 468 469void Thread::SignalBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason unblock_immediately_reason) 470{ 471 if (unblock_immediately_reason != UnblockImmediatelyReason::TimeoutInThePast) 472 return; 473 // If the specified timeout is 0 the caller is simply trying to poll once for pending signals, 474 // so simply calling check_pending_signals should populate the requested information. 475 check_pending_signals(false); 476} 477 478bool Thread::SignalBlocker::setup_blocker() 479{ 480 return add_to_blocker_set(thread().m_signal_blocker_set); 481} 482 483bool Thread::SignalBlocker::check_pending_signals(bool from_add_blocker) 484{ 485 { 486 SpinlockLocker lock(m_lock); 487 if (m_did_unblock) 488 return false; 489 490 auto pending_signals = thread().pending_signals() & m_pending_set; 491 492 // Also unblock if we have just "handled" that signal and are in the procecss 493 // of running their signal handler (i.e. we just unmarked the signal as pending). 494 if (thread().m_currently_handled_signal) 495 pending_signals |= (1 << (thread().m_currently_handled_signal - 1)) & m_pending_set; 496 497 auto matching_pending_signal = bit_scan_forward(pending_signals); 498 499 if (matching_pending_signal == 0) 500 return false; 501 502 m_did_unblock = true; 503 m_result = {}; 504 m_result.si_signo = matching_pending_signal; 505 m_result.si_code = 0; // FIXME: How can we determine this? 506 } 507 508 if (!from_add_blocker) 509 unblock_from_blocker(); 510 return true; 511} 512 513Thread::WaitBlockerSet::ProcessBlockInfo::ProcessBlockInfo(NonnullLockRefPtr<Process>&& process, WaitBlocker::UnblockFlags flags, u8 signal) 514 : process(move(process)) 515 , flags(flags) 516 , signal(signal) 517{ 518} 519 520Thread::WaitBlockerSet::ProcessBlockInfo::~ProcessBlockInfo() = default; 521 522void Thread::WaitBlockerSet::try_unblock(Thread::WaitBlocker& blocker) 523{ 524 SpinlockLocker lock(m_lock); 525 // We if we have any processes pending 526 for (size_t i = 0; i < m_processes.size(); i++) { 527 auto& info = m_processes[i]; 528 // We need to call unblock as if we were called from add_blocker 529 // so that we don't trigger a context switch by yielding! 530 if (info.was_waited && blocker.is_wait()) 531 continue; // This state was already waited on, do not unblock 532 if (blocker.unblock(info.process, info.flags, info.signal, true)) { 533 if (blocker.is_wait()) { 534 if (info.flags == Thread::WaitBlocker::UnblockFlags::Terminated) { 535 m_processes.remove(i); 536 dbgln_if(WAITBLOCK_DEBUG, "WaitBlockerSet[{}] terminated, remove {}", m_process, *info.process); 537 } else { 538 dbgln_if(WAITBLOCK_DEBUG, "WaitBlockerSet[{}] terminated, mark as waited {}", m_process, *info.process); 539 info.was_waited = true; 540 } 541 } 542 break; 543 } 544 } 545} 546 547void Thread::WaitBlockerSet::disowned_by_waiter(Process& process) 548{ 549 SpinlockLocker lock(m_lock); 550 if (m_finalized) 551 return; 552 for (size_t i = 0; i < m_processes.size();) { 553 auto& info = m_processes[i]; 554 if (info.process == &process) { 555 unblock_all_blockers_whose_conditions_are_met_locked([&](Blocker& b, void*, bool&) { 556 VERIFY(b.blocker_type() == Blocker::Type::Wait); 557 auto& blocker = static_cast<WaitBlocker&>(b); 558 bool did_unblock = blocker.unblock(info.process, WaitBlocker::UnblockFlags::Disowned, 0, false); 559 VERIFY(did_unblock); // disowning must unblock everyone 560 return true; 561 }); 562 dbgln_if(WAITBLOCK_DEBUG, "WaitBlockerSet[{}] disowned {}", m_process, *info.process); 563 m_processes.remove(i); 564 continue; 565 } 566 567 i++; 568 } 569} 570 571bool Thread::WaitBlockerSet::unblock(Process& process, WaitBlocker::UnblockFlags flags, u8 signal) 572{ 573 VERIFY(flags != WaitBlocker::UnblockFlags::Disowned); 574 575 bool did_unblock_any = false; 576 bool did_wait = false; 577 bool was_waited_already = false; 578 579 SpinlockLocker lock(m_lock); 580 if (m_finalized) 581 return false; 582 if (flags != WaitBlocker::UnblockFlags::Terminated) { 583 // First check if this state was already waited on 584 for (auto& info : m_processes) { 585 if (info.process == &process) { 586 was_waited_already = info.was_waited; 587 break; 588 } 589 } 590 } 591 592 unblock_all_blockers_whose_conditions_are_met_locked([&](Blocker& b, void*, bool&) { 593 VERIFY(b.blocker_type() == Blocker::Type::Wait); 594 auto& blocker = static_cast<WaitBlocker&>(b); 595 if (was_waited_already && blocker.is_wait()) 596 return false; // This state was already waited on, do not unblock 597 if (blocker.unblock(process, flags, signal, false)) { 598 did_wait |= blocker.is_wait(); // anyone requesting a wait 599 did_unblock_any = true; 600 return true; 601 } 602 return false; 603 }); 604 605 // If no one has waited (yet), or this wasn't a wait, or if it's anything other than 606 // UnblockFlags::Terminated then add it to your list 607 if (!did_unblock_any || !did_wait || flags != WaitBlocker::UnblockFlags::Terminated) { 608 bool updated_existing = false; 609 for (auto& info : m_processes) { 610 if (info.process == &process) { 611 VERIFY(info.flags != WaitBlocker::UnblockFlags::Terminated); 612 info.flags = flags; 613 info.signal = signal; 614 info.was_waited = did_wait; 615 dbgln_if(WAITBLOCK_DEBUG, "WaitBlockerSet[{}] update {} flags={}, waited={}", m_process, process, (int)flags, info.was_waited); 616 updated_existing = true; 617 break; 618 } 619 } 620 if (!updated_existing) { 621 dbgln_if(WAITBLOCK_DEBUG, "WaitBlockerSet[{}] add {} flags: {}", m_process, process, (int)flags); 622 m_processes.append(ProcessBlockInfo(process, flags, signal)); 623 } 624 } 625 return did_unblock_any; 626} 627 628bool Thread::WaitBlockerSet::should_add_blocker(Blocker& b, void*) 629{ 630 // NOTE: m_lock is held already! 631 if (m_finalized) 632 return false; 633 VERIFY(b.blocker_type() == Blocker::Type::Wait); 634 auto& blocker = static_cast<WaitBlocker&>(b); 635 // See if we can match any process immediately 636 for (size_t i = 0; i < m_processes.size(); i++) { 637 auto& info = m_processes[i]; 638 if (blocker.unblock(info.process, info.flags, info.signal, true)) { 639 // Only remove the entry if UnblockFlags::Terminated 640 if (info.flags == Thread::WaitBlocker::UnblockFlags::Terminated && blocker.is_wait()) 641 m_processes.remove(i); 642 return false; 643 } 644 } 645 return true; 646} 647 648void Thread::WaitBlockerSet::finalize() 649{ 650 SpinlockLocker lock(m_lock); 651 VERIFY(!m_finalized); 652 m_finalized = true; 653 654 // Clear the list of threads here so we can drop the references to them 655 m_processes.clear(); 656 657 // NOTE: Kernel processes don't have a leaked ref on them. 658 if (!m_process.is_kernel_process()) { 659 // No more waiters, drop the last reference immediately. This may 660 // cause us to be destructed ourselves! 661 VERIFY(m_process.ref_count() > 0); 662 m_process.unref(); 663 } 664} 665 666Thread::WaitBlocker::WaitBlocker(int wait_options, Variant<Empty, NonnullLockRefPtr<Process>, NonnullLockRefPtr<ProcessGroup>> waitee, ErrorOr<siginfo_t>& result) 667 : m_wait_options(wait_options) 668 , m_result(result) 669 , m_waitee(move(waitee)) 670{ 671} 672 673bool Thread::WaitBlocker::setup_blocker() 674{ 675 if (m_wait_options & WNOHANG) 676 return false; 677 return add_to_blocker_set(Process::current().wait_blocker_set()); 678} 679 680void Thread::WaitBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason) 681{ 682 Process::current().wait_blocker_set().try_unblock(*this); 683} 684 685void Thread::WaitBlocker::was_unblocked(bool) 686{ 687 bool got_sigchld, try_unblock; 688 { 689 SpinlockLocker lock(m_lock); 690 try_unblock = !m_did_unblock; 691 got_sigchld = m_got_sigchild; 692 } 693 694 if (try_unblock) 695 Process::current().wait_blocker_set().try_unblock(*this); 696 697 // If we were interrupted by SIGCHLD (which gets special handling 698 // here) we're not going to return with EINTR. But we're going to 699 // deliver SIGCHLD (only) here. 700 auto* current_thread = Thread::current(); 701 if (got_sigchld && current_thread->state() != State::Stopped) 702 current_thread->try_dispatch_one_pending_signal(SIGCHLD); 703} 704 705void Thread::WaitBlocker::do_was_disowned() 706{ 707 VERIFY(!m_did_unblock); 708 m_did_unblock = true; 709 m_result = ECHILD; 710} 711 712void Thread::WaitBlocker::do_set_result(siginfo_t const& result) 713{ 714 VERIFY(!m_did_unblock); 715 m_did_unblock = true; 716 m_result = result; 717 718 if (do_get_interrupted_by_signal() == SIGCHLD) { 719 // This makes it so that wait() will return normally despite the 720 // fact that SIGCHLD was delivered. Calling do_clear_interrupted_by_signal 721 // will disable dispatching signals in Thread::block and prevent 722 // it from returning with EINTR. We will then manually dispatch 723 // SIGCHLD (and only SIGCHLD) in was_unblocked. 724 m_got_sigchild = true; 725 do_clear_interrupted_by_signal(); 726 } 727} 728 729bool Thread::WaitBlocker::unblock(Process& process, UnblockFlags flags, u8 signal, bool from_add_blocker) 730{ 731 VERIFY(flags != UnblockFlags::Terminated || signal == 0); // signal argument should be ignored for Terminated 732 733 bool do_not_unblock = m_waitee.visit( 734 [&](NonnullLockRefPtr<Process> const& waitee_process) { 735 return &process != waitee_process; 736 }, 737 [&](NonnullLockRefPtr<ProcessGroup> const& waitee_process_group) { 738 return waitee_process_group->pgid() != process.pgid(); 739 }, 740 [&](Empty const&) { 741 // Generic waiter won't be unblocked by disown 742 return flags == UnblockFlags::Disowned; 743 }); 744 745 if (do_not_unblock) 746 return false; 747 748 switch (flags) { 749 case UnblockFlags::Terminated: 750 if (!(m_wait_options & WEXITED)) 751 return false; 752 break; 753 case UnblockFlags::Stopped: 754 if (!(m_wait_options & WSTOPPED)) 755 return false; 756 if (!(m_wait_options & WUNTRACED) && !process.is_traced()) 757 return false; 758 break; 759 case UnblockFlags::Continued: 760 if (!(m_wait_options & WCONTINUED)) 761 return false; 762 if (!(m_wait_options & WUNTRACED) && !process.is_traced()) 763 return false; 764 break; 765 case UnblockFlags::Disowned: 766 SpinlockLocker lock(m_lock); 767 // Disowning must unblock anyone waiting for this process explicitly 768 if (!m_did_unblock) 769 do_was_disowned(); 770 return true; 771 } 772 773 if (flags == UnblockFlags::Terminated) { 774 VERIFY(process.is_dead()); 775 776 SpinlockLocker lock(m_lock); 777 if (m_did_unblock) 778 return false; 779 // Up until this point, this function may have been called 780 // more than once! 781 do_set_result(process.wait_info()); 782 } else { 783 siginfo_t siginfo {}; 784 { 785 SpinlockLocker lock(g_scheduler_lock); 786 auto credentials = process.credentials(); 787 // We need to gather the information before we release the scheduler lock! 788 siginfo.si_signo = SIGCHLD; 789 siginfo.si_pid = process.pid().value(); 790 siginfo.si_uid = credentials->uid().value(); 791 siginfo.si_status = signal; 792 793 switch (flags) { 794 case UnblockFlags::Terminated: 795 case UnblockFlags::Disowned: 796 VERIFY_NOT_REACHED(); 797 case UnblockFlags::Stopped: 798 siginfo.si_code = CLD_STOPPED; 799 break; 800 case UnblockFlags::Continued: 801 siginfo.si_code = CLD_CONTINUED; 802 break; 803 } 804 } 805 806 SpinlockLocker lock(m_lock); 807 if (m_did_unblock) 808 return false; 809 // Up until this point, this function may have been called 810 // more than once! 811 do_set_result(siginfo); 812 } 813 814 if (!from_add_blocker) { 815 // Only call unblock if we weren't called from within add_to_blocker_set! 816 VERIFY(flags != UnblockFlags::Disowned); 817 unblock_from_blocker(); 818 } 819 // Because this may be called from add_blocker, in which case we should 820 // not be actually trying to unblock the thread (because it hasn't actually 821 // been blocked yet), we need to return true anyway 822 return true; 823} 824 825Thread::FlockBlocker::FlockBlocker(NonnullRefPtr<Inode> inode, flock const& flock) 826 : m_inode(move(inode)) 827 , m_flock(flock) 828{ 829} 830 831void Thread::FlockBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason) 832{ 833 VERIFY(reason == UnblockImmediatelyReason::UnblockConditionAlreadyMet); 834} 835 836bool Thread::FlockBlocker::setup_blocker() 837{ 838 return add_to_blocker_set(m_inode->flock_blocker_set()); 839} 840 841bool Thread::FlockBlocker::try_unblock(bool from_add_blocker) 842{ 843 if (!m_inode->can_apply_flock(m_flock)) 844 return false; 845 846 { 847 SpinlockLocker lock(m_lock); 848 if (m_did_unblock) 849 return false; 850 m_did_unblock = true; 851 } 852 853 if (!from_add_blocker) 854 unblock_from_blocker(); 855 return true; 856} 857 858}