Serenity Operating System
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}