24#include "pedigree/kernel/Atomic.h"
25#include "pedigree/kernel/LockGuard.h"
26#include "pedigree/kernel/Log.h"
27#include "pedigree/kernel/Metrics.h"
28#include "pedigree/kernel/Spinlock.h"
29#include "pedigree/kernel/Subsystem.h"
30#include "pedigree/kernel/process/PerProcessorScheduler.h"
31#include "pedigree/kernel/process/Process.h"
32#include "pedigree/kernel/process/Scheduler.h"
33#include "pedigree/kernel/process/Semaphore.h"
34#include "pedigree/kernel/process/TerminationDeferral.h"
35#include "pedigree/kernel/process/Thread.h"
36#include "pedigree/kernel/processor/Processor.h"
37#include "pedigree/kernel/processor/ProcessorInformation.h"
38#include "pedigree/kernel/processor/VirtualAddressSpace.h"
39#include "pedigree/kernel/processor/types.h"
40#include "pedigree/kernel/time/Time.h"
41#include "pedigree/kernel/utilities/Iterator.h"
42#include "pedigree/kernel/utilities/List.h"
43#include "pedigree/kernel/utilities/MemoryAllocator.h"
44#include "pedigree/kernel/utilities/StaticString.h"
45#include "pedigree/kernel/utilities/Vector.h"
46#include "pedigree/kernel/utilities/ZombieQueue.h"
47#include "pedigree/kernel/utilities/utility.h"
49#include "modules/system/users/Group.h"
50#include "modules/system/users/User.h"
51#include "modules/system/vfs/File.h"
57 if (!g_DefaultUserspacePidNamespace) {
60 return g_DefaultUserspacePidNamespace;
65 for (
auto current = space; current; current = current->parent()) {
66 m_Ids.pushBack(current->allocate());
72 space = m_Namespace.
get();
75 for (
auto current = m_Namespace; current; current = current->parent(), ++index) {
76 if (current.get() == space) {
84 return defaultUserspacePidNamespace();
89 return m_ChildrenPidNamespace ? m_ChildrenPidNamespace
91 : defaultUserspacePidNamespace();
94bool Process::pidNamespaceReady()
const {
106bool Process::createPidNamespace() {
122bool Process::unsharePidNamespace() {
127 return unsharePidNamespace(space);
132 prepared->depth() > 32 || prepared->dead()) {
136 if (m_ChildrenPidNamespace) {
139 m_ChildrenPidNamespace = prepared;
145 for (
auto space = process->pidNamespace(); space; space = space->parent()) {
146 Process* candidate = space->init();
147 if (!space->dead() && candidate != process &&
151 if (reaper->pidNamespace().
get() == space.get() && reaper->
getUserspaceId() == 1 &&
170#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
171Process::TerminationElectionHook Process::m_TerminationElectionHook =
nullptr;
172Process::ExternalLeaseReleaseHook Process::m_ExternalLeaseReleaseHook =
nullptr;
173Process* Process::m_ExternalLeaseReleaseTarget =
nullptr;
174Process::OrphanPublicationHook Process::m_OrphanPublicationHook =
nullptr;
179 if (!pProcess || pProcess == pExcluded) {
184 return state == Process::Active || state == Process::Suspended;
189Process::ExecScope::ExecScope(
Process& process,
bool active)
190 : m_pProcess(nullptr), m_bAdmitted(!active), m_TerminationDeferral(active) {
200 m_pProcess = &process;
205Process::ExecScope::~ExecScope() {
211 if (m_pProcess->m_bExecCommitted) {
215 m_pProcess->m_bExecCommitted =
false;
216 m_pProcess->m_bExecExitForwarded =
false;
219 m_pProcess->m_ExecWaiters.wakeAll();
222Process::ThreadCreationScope::ThreadCreationScope(
Process& process)
223 : m_pProcess(nullptr), m_TerminationDeferral(true) {
226 ++process.m_nThreadCreations;
227 m_pProcess = &process;
231Process::ThreadCreationScope::~ThreadCreationScope() {
237 --m_pProcess->m_nThreadCreations;
239 m_pProcess->m_ExecWaiters.wakeAll();
242bool Process::ExecScope::commit() {
246 Process& process = *m_pProcess;
250 auto progress = process.m_ExecWaiters.acquire();
251 bool creatorsFinished =
false;
258 creatorsFinished = process.m_nThreadCreations == 0;
259 if (creatorsFinished) {
262 process.m_bExecCommitted =
true;
264 auto exitGuard = thread->m_JoinWaiters.acquire();
265 if (thread != current && thread->m_bReapable &&
266 (thread->m_bDetachedRetirementClaimed || thread->m_bJoinClaimed)) {
269 thread->m_bProcessExitOwned =
true;
270 if (!thread->m_bReapable) {
271 thread->m_bProcessExitParticipant =
true;
274 if (thread != current) {
282 if (creatorsFinished) {
285 const WaitQueue::WakeReason reason = progress.waitForCompletion(
294 for (
Thread* peer : peers) {
298 auto progress = process.m_ExecWaiters.acquire();
299 bool peersOffStack =
false;
307 const WaitQueue::WakeReason reason = progress.waitForCompletion(
320 WaitQueue::Channel(&process), Thread::ProcessWait,
reinterpret_cast<uintptr_t
>(&process));
323 for (
Thread* peer : peers) {
324 peer->closeExternalLeaseAdmissionAndDrain();
328 auto progress = process.m_ExecWaiters.acquire();
340 const WaitQueue::WakeReason reason = progress.waitForCompletion(
346void Process::ExecScope::adoptLeaderIdentity() {
349 __atomic_store_n(&m_pProcess->m_pExecOwner->m_TaskId, m_pProcess->m_Id, __ATOMIC_RELEASE);
353Process::ThreadLease::ThreadLease()
354 : m_pProcess(nullptr), m_pThread(nullptr), m_TerminationDeferral(false) {}
356Process::ReaperClaim::ReaperClaim() : m_pProcess(nullptr), m_TerminationDeferral(false) {}
358Process::ReaperClaim::ReaperClaim(
Process* process)
359 : m_pProcess(process), m_TerminationDeferral(process != nullptr) {}
361Process::ReaperClaim::ReaperClaim(ReaperClaim&& other) noexcept
362 : m_pProcess(other.m_pProcess),
363 m_TerminationDeferral(pedigree_std::move(other.m_TerminationDeferral)) {
364 other.m_pProcess =
nullptr;
367Process::ReaperClaim::~ReaperClaim() {
369 FATAL(
"Process reaper ownership left scope without publication");
374 if (
this != &other) {
376 FATAL(
"Process reaper ownership overwritten before publication");
378 m_pProcess = other.m_pProcess;
379 m_TerminationDeferral = pedigree_std::move(other.m_TerminationDeferral);
380 other.m_pProcess =
nullptr;
385void Process::ReaperClaim::publish() {
387 FATAL(
"Invalid Process reaper publication");
391 m_pProcess =
nullptr;
396Process::TerminalOwnerReservation::TerminalOwnerReservation()
397 : m_pProcess(nullptr), m_TerminationDeferral(false) {}
399Process::TerminalOwnerReservation::TerminalOwnerReservation(
400 TerminalOwnerReservation&& other) noexcept
401 : m_pProcess(other.m_pProcess),
402 m_TerminationDeferral(pedigree_std::move(other.m_TerminationDeferral)) {
403 other.m_pProcess =
nullptr;
406Process::TerminalOwnerReservation::~TerminalOwnerReservation() {
408 FATAL(
"Process terminal-owner reservation left scope without installation");
413 TerminalOwnerReservation&& other)
noexcept {
414 if (
this != &other) {
416 FATAL(
"Process terminal-owner reservation overwritten before installation");
418 m_pProcess = other.m_pProcess;
419 m_TerminationDeferral = pedigree_std::move(other.m_TerminationDeferral);
420 other.m_pProcess =
nullptr;
426 if (!m_pProcess || !owner) {
427 FATAL(
"Invalid Process terminal-owner installation");
432 m_pProcess =
nullptr;
436Process::ThreadLease::ThreadLease(
Process* process,
Thread* thread)
437 : m_pProcess(process), m_pThread(thread), m_TerminationDeferral(process && thread) {}
439Process::ThreadLease::ThreadLease(ThreadLease&& other) noexcept
440 : m_pProcess(other.m_pProcess),
441 m_pThread(other.m_pThread),
442 m_TerminationDeferral(pedigree_std::move(other.m_TerminationDeferral)) {
443 other.m_pProcess =
nullptr;
444 other.m_pThread =
nullptr;
447Process::ThreadLease::~ThreadLease() {
452 if (
this != &other) {
453 if (other.m_pProcess && other.m_pThread) {
454 m_TerminationDeferral = pedigree_std::move(other.m_TerminationDeferral);
456 Process* previousProcess = m_pProcess;
457 Thread* previousThread = m_pThread;
458 m_pProcess = other.m_pProcess;
459 m_pThread = other.m_pThread;
460 other.m_pProcess =
nullptr;
461 other.m_pThread =
nullptr;
462 if (previousProcess) {
472void Process::ThreadLease::reset() {
474 Thread* thread = m_pThread;
475 m_pProcess =
nullptr;
483Process::FileContextLease::FileContextLease()
484 : m_pFile(nullptr), m_TerminationDeferral(false), m_Context() {}
486Process::FileContextLease::~FileContextLease() =
default;
488void Process::FileContextLease::reset() {
501 m_Id(
Scheduler::instance().reserveProcessId()),
502 m_UserspaceNamespace(),
508 m_FilesystemContextLock(),
509 m_FilesystemContext(),
510 m_bFilesystemContextReady(true),
512 m_SpaceAllocator(false),
513 m_DynamicSpaceAllocator(false),
514 m_UserReservationLock(false),
515 m_UserReservationGeneration(0),
519 m_pEffectiveGroup(0),
522 m_ChildStateWaiters(),
523 m_TerminationWaiters(),
524 m_SuspensionWaiters(),
525 m_ContinuationEpoch(0),
526 m_ThreadJoinWaiters(),
527 m_nThreadJoinOperations(0),
528 m_bThreadJoinAdmissionClosed(false),
529 m_ExternalLeaseLock(false),
530 m_ExternalLeaseWaiters(),
531 m_nExternalLeases(0),
532 m_bExternalLeaseAdmissionClosed(false),
533 m_bExternalLeaseReleaseInProgress(false),
534 m_PendingChildTransition(),
537 m_bDestroying(false),
539 m_bUnregistered(false),
540 m_pTerminatingThread(0),
541 m_nTerminationParticipants(0),
542 m_bTerminationRendezvousStarted(false),
543 m_bTerminalOwnerReserved(false),
544 m_pReservedTerminalOwner(nullptr),
545 m_bTerminationCleanupStarted(false),
546 m_bTerminationSealed(false),
547 m_bTerminationReapable(false),
548 m_ReaperState(ReaperUnclaimed),
550 m_TimeAccountingLock(false),
552 m_DeferredTimeAccounting(),
553 m_TimeAccountingReports(),
554 m_bTimeAccountingReportsEnabled(false),
555 m_TimeAccountingReportInterest(0),
556 m_bSharedAddressSpace(false) {
558 m_Metadata.
startTime = Time::getTimeNanoseconds();
560 resetUserReservations();
561 Metrics::increment(Metrics::ProcessCreated);
570 FilesystemContextMode filesystemContext,
bool emptyAddressSpace, ProcessType type)
573 m_Id(
Scheduler::instance().reserveProcessId()),
574 m_UserspaceNamespace(pParent->pidNamespaceForChildren()),
580 m_FilesystemContextLock(),
581 m_FilesystemContext(),
582 m_bFilesystemContextReady(true),
584 m_SpaceAllocator(false),
585 m_DynamicSpaceAllocator(false),
586 m_UserReservationLock(false),
587 m_UserReservationGeneration(0),
588 m_pUser(pParent->m_pUser),
589 m_pGroup(pParent->m_pGroup),
590 m_pEffectiveUser(pParent->m_pEffectiveUser),
591 m_pEffectiveGroup(pParent->m_pEffectiveGroup),
592 m_pDynamicLinker(pParent->m_pDynamicLinker),
594 m_ChildStateWaiters(),
595 m_TerminationWaiters(),
596 m_SuspensionWaiters(),
597 m_ContinuationEpoch(0),
598 m_ThreadJoinWaiters(),
599 m_nThreadJoinOperations(0),
600 m_bThreadJoinAdmissionClosed(false),
601 m_ExternalLeaseLock(false),
602 m_ExternalLeaseWaiters(),
603 m_nExternalLeases(0),
604 m_bExternalLeaseAdmissionClosed(false),
605 m_bExternalLeaseReleaseInProgress(false),
606 m_PendingChildTransition(),
609 m_bDestroying(false),
611 m_bUnregistered(false),
612 m_pTerminatingThread(0),
613 m_nTerminationParticipants(0),
614 m_bTerminationRendezvousStarted(false),
615 m_bTerminalOwnerReserved(false),
616 m_pReservedTerminalOwner(nullptr),
617 m_bTerminationCleanupStarted(false),
618 m_bTerminationSealed(false),
619 m_bTerminationReapable(false),
620 m_ReaperState(ReaperUnclaimed),
622 m_TimeAccountingLock(false),
624 m_DeferredTimeAccounting(),
625 m_TimeAccountingReports(),
626 m_bTimeAccountingReportsEnabled(false),
627 m_TimeAccountingReportInterest(0),
628 m_bSharedAddressSpace(!bCopyOnWrite) {
629 UserReservationSnapshot inheritedReservations;
630 if (!pParent->snapshotUserReservations(inheritedReservations)) {
631 FATAL(
"Cannot snapshot parent process reservations");
641 if (filesystemContext == FilesystemContextMode::Inherit) {
642 auto parentContext = pParent->acquireFilesystemContext();
644 m_bFilesystemContextReady = parentContext->forkForProcess(m_FilesystemContext);
646 m_bFilesystemContextReady =
false;
653 m_Metadata.
virtualPages = pParent->getVirtualPageCount();
655 m_Metadata.
sharedPages = pParent->getSharedPageCount();
656 m_Metadata.
startTime = Time::getTimeNanoseconds();
658#if PEDIGREE_BENCHMARK_SYSCALL_TIMING
659 m_BenchmarkSyscallTiming = __atomic_load_n(&pParent->m_BenchmarkSyscallTiming, __ATOMIC_ACQUIRE);
661#if PEDIGREE_BENCHMARK_SYSCALL_TRACE
662 m_BenchmarkSyscallTrace = __atomic_load_n(&pParent->m_BenchmarkSyscallTrace, __ATOMIC_ACQUIRE);
664#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
665 m_BenchmarkVmDiagnostics = __atomic_load_n(&pParent->m_BenchmarkVmDiagnostics, __ATOMIC_ACQUIRE);
671 resetUserReservations();
680 Metrics::increment(Metrics::ProcessCreated);
683void Process::VforkCompletion::wait() {
687 auto guard = m_Waiters.acquire();
691 reinterpret_cast<uintptr_t
>(
this));
696void Process::VforkCompletion::complete() {
697 auto guard = m_Waiters.acquire();
702void Process::borrowVforkAddressSpace(
Process& parent,
708 m_VforkCompletion = completion;
717 m_pVforkPrivateAddressSpace =
nullptr;
718 m_pVforkOwner =
nullptr;
720 if (current && current->
getParent() ==
this)
725 auto completion = pedigree_std::move(m_VforkCompletion);
726 completion->complete();
731 return m_FilesystemContext.reference();
738 if (m_FilesystemContext)
741 m_FilesystemContext = pedigree_std::move(context);
742 m_bFilesystemContextReady =
true;
754 if (m_FilesystemContext.reference().
get() != expected.
get()) {
757 retired = pedigree_std::move(m_FilesystemContext);
758 m_FilesystemContext = pedigree_std::move(replacement);
764bool Process::filesystemContextReady()
const {
766 return m_bFilesystemContextReady;
769void Process::releaseFilesystemContext() {
773 retired = pedigree_std::move(m_FilesystemContext);
774 m_bFilesystemContextReady =
false;
782 explicit CoreControllingTerminal(
File* file) : m_File(file) {}
783 ~CoreControllingTerminal()
override {
784 m_File->releaseVfsReference();
786 File* file()
const override {
800File* Process::acquireCtty(FileContextLease& lease)
const {
802 lease.m_Context = acquireCttyContext();
803 lease.m_pFile = lease.m_Context ? lease.m_Context->file() :
nullptr;
804 if (!lease.m_pFile) {
815 retired = pedigree_std::move(
m_Ctty);
827 auto* raw =
new CoreControllingTerminal(file);
835 setCttyContext(context);
840 __atomic_store_n(&m_TimeAccountingReportInterest, initialInterest, __ATOMIC_RELEASE);
845 const size_t previous =
846 enabled ? __atomic_fetch_or(&m_TimeAccountingReportInterest, interest, __ATOMIC_ACQ_REL)
847 : __atomic_fetch_and(&m_TimeAccountingReportInterest, ~interest, __ATOMIC_ACQ_REL);
850 if (enabled && interest && !previous &&
858 Time::Timestamp& kernel) {
859 if (!child || child ==
this || child->getState() !=
Reaped) {
860 FATAL(
"Process child CPU accounting requires sole ownership of a reaped child");
863 user = child->
getUserTime() + child->getReapedChildrenUserTime();
864 kernel = child->getKernelTime() + child->getReapedChildrenKernelTime();
867#if PEDIGREE_SYSCALL_COUNTER
868 const uint64_t syscalls = child->getSyscallCount() + child->getReapedChildrenSyscallCount();
869 __atomic_fetch_add(&m_Metadata.reapedChildrenSyscallCount, syscalls, __ATOMIC_RELAXED);
870 SyscallLatencySnapshot latency;
871 child->getSyscallLatencySnapshot(latency);
872 for (
size_t i = 0; i < SyscallLatencyBucketCount; ++i) {
873 __atomic_fetch_add(&m_Metadata.reapedChildrenSyscallLatencyBuckets[i], latency.buckets[i],
877#if PEDIGREE_BENCHMARK_SYSCALL_TIMING
878 for (
size_t i = 0; i < SyscallTimingSlotCount; ++i) {
879 const uint64_t calls = __atomic_load_n(&child->m_SyscallTimingCalls[i], __ATOMIC_ACQUIRE);
880 const uint64_t kernelNanoseconds =
881 __atomic_load_n(&child->m_SyscallTimingKernelNanoseconds[i], __ATOMIC_ACQUIRE);
882 __atomic_fetch_add(&m_SyscallTimingCalls[i], calls, __ATOMIC_RELAXED);
883 __atomic_fetch_add(&m_SyscallTimingKernelNanoseconds[i], kernelNanoseconds, __ATOMIC_RELAXED);
886#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
887 for (
size_t i = 0; i < BenchmarkVmCounterCount; ++i) {
888 const uint64_t value = __atomic_load_n(&child->m_BenchmarkVmCounters[i], __ATOMIC_ACQUIRE);
889 __atomic_fetch_add(&m_BenchmarkVmCounters[i], value, __ATOMIC_RELAXED);
894void Process::queueTimeAccountingReport(Time::Timestamp elapsed) {
902 const Time::Timestamp* retired =
903 mode == CpuTimeMode::User ? &m_Metadata.
userTime : &m_Metadata.kernelTime;
904 Time::Timestamp total = __atomic_load_n(retired, __ATOMIC_ACQUIRE);
907 total += mode == CpuTimeMode::User ? thread->
getUserTime() : thread->getKernelTime();
913#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
914void Process::publishTimeAccountingForHostedTest(Time::Timestamp user, Time::Timestamp system) {
915 __atomic_fetch_add(&m_Metadata.
userTime, user, __ATOMIC_RELAXED);
916 __atomic_fetch_add(&m_Metadata.kernelTime, system, __ATOMIC_RELAXED);
917 reportTimeAccounting(user ? user : system);
920void Process::closeTimeAccountingForHostedTest() {
945 if (!filesystemContextReady())
946 FATAL(
"Process published before filesystem-context preparation completed");
948 FATAL(
"Process::publish() called more than once.");
956 if (!pRequestedParent) {
965 const bool initAcquired = acquireNamespaceReaper(
this, init);
967 initAcquired && canAdoptChildren(init.get(),
this) ? init.get() :
nullptr;
970 if (canAdoptChildren(publishParent,
this)) {
971 __atomic_store_n(&
m_pParent, publishParent, __ATOMIC_RELEASE);
974 publishGuard.wakeAll();
978 __atomic_store_n(&
m_pParent,
static_cast<Process*
>(
nullptr), __ATOMIC_RELEASE);
988 Process* pPublishParent = pRequestedParent;
989 if (!canAdoptChildren(pPublishParent)) {
990 pPublishParent =
nullptr;
993 if (!pPublishParent) {
995 const bool publishParentAcquired = acquireNamespaceReaper(
this, publishParent);
996 if (publishParentAcquired && publishParent.get() != pRequestedParent &&
997 canAdoptChildren(publishParent.get(),
this)) {
999 pPublishParent = publishParent.get();
1000 __atomic_store_n(&
m_pParent, pPublishParent, __ATOMIC_RELEASE);
1003 parentGuard.wakeAll();
1004 publishGuard.wakeAll();
1009 if (!canAdoptChildren(pPublishParent,
this)) {
1012 __atomic_store_n(&
m_pParent, pPublishParent, __ATOMIC_RELEASE);
1015 parentGuard.wakeAll();
1018#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1019void Process::makeOrphanBeforePublicationForHostedTest() {
1022 "Process test fixture attempted to become an orphan after "
1025 __atomic_store_n(&
m_pParent,
static_cast<Process*
>(
nullptr), __ATOMIC_RELEASE);
1037 __ATOMIC_RELEASE, __ATOMIC_RELAXED);
1048 bool unregistered =
false;
1049 while (!unregistered) {
1058 "Process retained an unpinned parent during "
1069 unregistered =
true;
1073 unregistered =
true;
1123 __atomic_fetch_add(&m_Metadata.kernelTime, thread->getKernelTime(), __ATOMIC_RELAXED);
1133 FATAL(
"Process::~Process invariant failed for pid "
1134 <<
Dec <<
m_Id <<
", tid " << pThread->
getId() <<
": thread status "
1135 <<
static_cast<size_t>(pThread->
getStatus()) <<
" is not off-stack/reapable.");
1138 FATAL(
"Process::~Process invariant failed for pid "
1140 <<
": a join claim escaped the lifetime barrier.");
1143 FATAL(
"Process::~Process invariant failed for pid "
1144 <<
Dec <<
m_Id <<
": destructor is running on a thread stack it would free.");
1157 releaseFilesystemContext();
1175 Metrics::increment(Metrics::ProcessDestroyed);
1191 FATAL(
"Process::endThreadJoin underflow for pid " <<
Dec <<
m_Id <<
".");
1212 bool finalRelease =
false;
1213 bool finishClosedRelease =
false;
1217 FATAL(
"Process external lease underflow for pid " <<
Dec <<
m_Id <<
".");
1223 wake = finishClosedRelease;
1224 if (finishClosedRelease) {
1229#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1230 Process* hookTarget = __atomic_load_n(&m_ExternalLeaseReleaseTarget, __ATOMIC_ACQUIRE);
1231 if (finalRelease && hookTarget ==
this) {
1232 ExternalLeaseReleaseHook hook = __atomic_load_n(&m_ExternalLeaseReleaseHook, __ATOMIC_ACQUIRE);
1234 hook(
this, ExternalLeaseFinalReleaseUnlocked);
1239 if (!finishClosedRelease) {
1240#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1241 ExternalLeaseReleaseHook hook = __atomic_load_n(&m_ExternalLeaseReleaseHook, __ATOMIC_ACQUIRE);
1242 if (wake && hookTarget ==
this && hook) {
1243 hook(
this, ExternalLeaseBeforeWaiterWake);
1263#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1264 ExternalLeaseReleaseHook hook = __atomic_load_n(&m_ExternalLeaseReleaseHook, __ATOMIC_ACQUIRE);
1265 if (hookTarget ==
this && hook) {
1266 hook(
this, ExternalLeaseBeforeWaiterWake);
1290 const WaitQueue::WakeReason reason = guard.waitForCompletion(
1310 FATAL(
"Process::addThread invariant failed for pid "
1311 <<
Dec <<
m_Id <<
": a thread cannot be published after exit rendezvous seals.");
1313 if (state == Terminating) {
1326 __atomic_store_n(&pThread->m_TaskId,
1329 pThread->m_UserspacePid = localId == 1
1332 Metrics::increment(Metrics::ThreadCreated);
1346 if (
m_pSubsystem && (state == Active || state == Suspended)) {
1363 if (*it == pThread) {
1367 __atomic_fetch_add(&m_Metadata.kernelTime, pThread->getKernelTime(), __ATOMIC_RELAXED);
1377 m_ExecWaiters.wakeAll();
1386 Thread* thread =
nullptr;
1408 Thread* target =
nullptr;
1413 auto acquire = [&](
Thread* thread) {
1427 if (acquire(thread)) {
1447 Thread* thread =
nullptr;
1451 if (*it && (*it)->getId() == id) {
1473bool Process::acquireThreadByTaskId(ThreadLease& lease,
size_t id) {
1474 Thread* thread =
nullptr;
1478 if (*it && (*it)->getTaskId() == id) {
1496 lease = ThreadLease(
this, thread);
1500bool Process::acquireThreadByUserspaceId(ThreadLease& lease,
size_t id,
1502 Thread* thread =
nullptr;
1506 if (*it && (*it)->getUserspaceTaskId(space) == id) {
1524 lease = ThreadLease(
this, thread);
1534 Thread* thread =
nullptr;
1538 if (*it == expected) {
1563 auto progress = m_ExecWaiters.acquire();
1565 if (m_bExecCommitted) {
1567 const WaitQueue::WakeReason reason = progress.waitForCompletion(
1574 state == Terminating || state == Terminated || state ==
Reaped) {
1579 FATAL(
"Process terminal owner reserved more than once for pid " <<
Dec <<
m_Id <<
".");
1584 reservation.m_pProcess =
this;
1595 FATAL(
"Process terminal owner installed without a reservation for pid " <<
Dec <<
m_Id <<
".");
1606 FATAL(
"Process terminal owner was not published to pid " <<
Dec <<
m_Id <<
".");
1611 if (owner->m_Status != Thread::Created || owner->m_bStartRequested ||
1613 FATAL(
"Process terminal owner was not installed before startup for pid " <<
Dec <<
m_Id
1622 return __atomic_compare_exchange_n(&
m_State, &expected, desired,
false, __ATOMIC_ACQ_REL,
1629 if (state == Terminating) {
1632 if (state != Active && state != Suspended) {
1633 FATAL(
"Process state cannot transition to Terminating from "
1634 <<
Dec <<
static_cast<size_t>(state) <<
" for pid " <<
m_Id <<
".");
1644 FATAL(
"Process state cannot transition to Reaped from "
1645 <<
Dec <<
static_cast<size_t>(getState()) <<
" for pid " <<
m_Id <<
".");
1649void Process::markTerminating() {
1656 if (!current || current->
getParent() !=
this) {
1657 FATAL(
"Thread exit intent must come from the current process.");
1692 if (!pCurrentThread || pCurrentThread->
getParent() !=
this) {
1693 FATAL(
"Process::beginTermination invariant failed for pid "
1694 <<
Dec <<
m_Id <<
": exit must be initiated by a thread in the target process.");
1699 if (m_bExecCommitted &&
m_pExecOwner != pCurrentThread) {
1700 if (m_bExecExitForwarded) {
1704 m_bExecExitForwarded =
true;
1707 FATAL(
"Exec owner lost its lifetime during exit forwarding.");
1711 if (cause == Subsystem::ExitCause::Signal) {
1712 owner->deferSignalExit(code);
1714 owner->deferProcessExit(code);
1732 "Process exit competitor was absent from the rendezvous for "
1734 <<
Dec <<
m_Id <<
", tid " << pCurrentThread->
getId() <<
".");
1741 "Process::beginTermination observed an invalid completed "
1742 "rendezvous for pid "
1748 bool currentThreadParticipates =
false;
1756 FATAL(
"Process::beginTermination invariant failed for pid "
1758 <<
": thread has inconsistent process-exit ownership.");
1767 if (pThread == pCurrentThread) {
1768 currentThreadParticipates =
true;
1770 peersToTerminate.
pushBack(pThread);
1774 if (!currentThreadParticipates) {
1775 FATAL(
"Process::beginTermination invariant failed for pid "
1776 <<
Dec <<
m_Id <<
": initiating thread is absent or already off-stack.");
1786 const bool closesPidNamespace =
1788 if (closesPidNamespace) {
1795 __ATOMIC_RELEASE, __ATOMIC_RELAXED);
1800 NOTICE(
"Kill: " <<
m_Id <<
" (parent: " << logParent->
getId() <<
")");
1802 NOTICE(
"Kill: " <<
m_Id <<
" (parent: <orphan>)");
1808 const bool newParentAcquired = acquireNamespaceReaper(
this, newParent);
1809 Process* pNewParent = newParentAcquired ? newParent.get() :
nullptr;
1810 auto moveChildren = [
this](
Process* pParent) {
1817 __atomic_store_n(&pChild->
m_pParent, pParent, __ATOMIC_RELEASE);
1825 if (!canAdoptChildren(pNewParent,
this)) {
1828 moveChildren(pNewParent);
1829 oldParentGuard.wakeAll();
1830 newParentGuard.wakeAll();
1833 oldParentGuard.wakeAll();
1842#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1843 if (m_TerminationElectionHook) {
1844 m_TerminationElectionHook(
this, pCurrentThread);
1848 if (closesPidNamespace) {
1852 after = member->
getId();
1858 member->getSubsystem()->
kill(Subsystem::Unknown, recipient.get());
1890 auto progress = m_ExecWaiters.acquire();
1896 if (m_nThreadCreations == 0) {
1901 const WaitQueue::WakeReason reason = progress.waitForCompletion(
1911 if (participants == 0) {
1912 FATAL(
"Process exit owner disappeared before teardown for pid " <<
Dec <<
m_Id <<
".");
1914 if (participants == 1) {
1918 const WaitQueue::WakeReason reason =
1920 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1936 "Process::finishTermination called without exclusive "
1937 "off-stack teardown ownership for pid "
1952 NOTICE(
"Process::kill() - process is an orphan, adding to ZombieQueue.");
1956#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1957 OrphanPublicationHook publicationHook =
1958 __atomic_load_n(&m_OrphanPublicationHook, __ATOMIC_ACQUIRE);
1959 if (publicationHook) {
1965#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1966 if (publicationHook) {
1978 "Process::finishTermination lost exclusive off-stack teardown "
1979 "ownership for pid "
1988 "Process::kill() - not adding to ZombieQueue, process has a "
1994 PerProcessorScheduler::StackDiscardReason::EmergencyProcessKill, &
m_Lock);
1998 FATAL(
"Should never get here");
2002 size_t expected = ReaperUnclaimed;
2003 if (!__atomic_compare_exchange_n(&
m_ReaperState, &expected, ReaperClaimed,
false,
2004 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
2011 size_t expected = ReaperClaimed;
2012 if (!__atomic_compare_exchange_n(&
m_ReaperState, &expected, ReaperPublished,
false,
2013 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
2014 FATAL(
"Process reaper ownership published more than once");
2022 PerProcessorScheduler::StackDiscardReason::EmergencyProcessKill);
2025 FATAL(
"Process::kill failed to claim teardown for pid " <<
Dec <<
m_Id <<
".");
2030#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
2031void Process::setTerminationElectionHook(TerminationElectionHook hook) {
2032 m_TerminationElectionHook = hook;
2035void Process::setExternalLeaseReleaseHookForHostedTest(
Process* target,
2036 ExternalLeaseReleaseHook hook) {
2037 __atomic_store_n(&m_ExternalLeaseReleaseTarget, target, __ATOMIC_RELEASE);
2038 __atomic_store_n(&m_ExternalLeaseReleaseHook, hook, __ATOMIC_RELEASE);
2041bool Process::isTerminationReapableForHostedTest() {
2046void Process::setOrphanPublicationHook(OrphanPublicationHook hook) {
2047 __atomic_store_n(&m_OrphanPublicationHook, hook, __ATOMIC_RELEASE);
2060 size_t continuationEpoch) {
2061 bool published =
false;
2062 bool enteredSuspended =
false;
2063 while (!published) {
2071 FATAL(
"Process::suspend retained an unpinned parent.");
2079 const bool epochMatches = !checkContinuationEpoch || continuationEpoch ==
m_ContinuationEpoch;
2083 enteredSuspended =
true;
2089 const bool epochMatches = !checkContinuationEpoch || continuationEpoch ==
m_ContinuationEpoch;
2093 enteredSuspended =
true;
2099 if (!enteredSuspended && getState() != Suspended) {
2104 if (enteredSuspended) {
2108 bool parentThreadAcquired =
false;
2115 if (parentThreadAcquired) {
2116 parent->getSubsystem()->
threadException(parentThread.get(), Subsystem::Child);
2122 if (current && current->
getParent() ==
this) {
2136 if (getState() != Suspended) {
2140 const WaitQueue::WakeReason wakeReason =
2142 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
2143 if (wakeReason == WaitQueue::WakeReason::Terminating ||
2144 wakeReason == WaitQueue::WakeReason::Unwinding) {
2156 bool published =
false;
2157 bool resumed =
false;
2158 while (!published) {
2166 FATAL(
"Process::resume retained an unpinned parent.");
2179 suspensionGuard.wakeAll();
2190 suspensionGuard.wakeAll();
2207 eventGuard.wakeAll(WaitQueue::WakeReason::Signalled,
2218 const bool selected =
2243int64_t Process::getGroupId()
const {
2248int64_t Process::getEffectiveUserId()
const {
2253int64_t Process::getEffectiveGroupId()
const {
2262void Process::setUserId(int64_t) {}
2264void Process::setGroupId(int64_t) {}
2266void Process::setEffectiveUserId(int64_t) {}
2268void Process::setEffectiveGroupId(int64_t) {}
2280 const WaitQueue::WakeReason wakeReason =
2282 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
2283 if (wakeReason == WaitQueue::WakeReason::Terminating ||
2284 wakeReason == WaitQueue::WakeReason::Unwinding) {
2314 FATAL(
"Process exit rendezvous underflow for pid " <<
Dec <<
m_Id <<
", tid "
2315 << pThread->
getId() <<
".");
2330 bool published =
false;
2331 while (!published) {
2340 "Process::publishTerminationStatus retained an unpinned "
2347 const bool parentThreadAcquired =
2349 bool parentAcceptsSignal =
false;
2357 parentAcceptsSignal = parentState == Active || parentState == Suspended;
2362 FATAL(
"Process state was not Terminating while publishing pid " <<
Dec <<
m_Id <<
".");
2364 Metrics::increment(Metrics::ProcessExited);
2370 if (parentThreadAcquired && parentAcceptsSignal && parent->getSubsystem()) {
2371 parent->getSubsystem()->
threadException(parentThread.get(), Subsystem::Child);
2377 FATAL(
"Process state was not Terminating while publishing pid " <<
Dec <<
m_Id <<
".");
2379 Metrics::increment(Metrics::ProcessExited);
2389 FATAL(
"Process termination published with "
2397 FATAL(
"Process termination published before tid " <<
Dec << pThread->
getId() <<
" of pid "
2398 <<
m_Id <<
" switched off-stack.");
2402 if (state != Terminated && state !=
Reaped) {
2403 FATAL(
"Process off-stack completion observed unpublished wait status for pid " <<
Dec <<
m_Id
2423 __atomic_compare_exchange_n(&
m_pInitProcess, &expected, pProcess,
false, __ATOMIC_RELEASE,
bool publish(Time::Timestamp elapsed)
virtual bool retainVfsReference()
virtual void releaseVfsReference()
MUST_USE_RESULT bool tryAcquire(Lease &lease)
MUST_USE_RESULT bool serviceProcessStopAtUserReturn(ProcessStopGateMode mode=ProcessStopGateMode::StopOnly)
void install(Thread *owner)
MUST_USE_RESULT bool setCtty(File *file)
virtual void reportTimesUpdated(Time::Timestamp userTotal, Time::Timestamp total)
size_t getUserspaceId() const
Group * getEffectiveGroup() const
bool beginTermination(int code=0, Subsystem::ExitCause cause=Subsystem::ExitCause::Normal)
void drainExternalLeases()
void closeDeferredTimeAccounting()
bool quiesceTermination()
@ Reaped
Terminal wait status is visible; the owner may still be on-stack.
ssize_t getHeapUsage() const
MUST_USE_RESULT bool installFilesystemContext(FilesystemContextOwner &&context)
bool m_bTerminalOwnerReserved
bool waitUntilTerminationReapableForTerminalCoordinator()
virtual void processTerminated()
MUST_USE_RESULT bool acquireProcessSignalThread(ThreadLease &lease)
bool beginExternalLease()
Time::Timestamp totalTime(CpuTimeMode mode) const
SharedPointer< UserspacePidNamespace > m_UserspaceNamespace
void closeExternalLeaseAdmission()
void publishTerminationReapable()
VirtualAddressSpace * getAddressSpace()
size_t m_nTerminationParticipants
size_t m_nThreadJoinOperations
void accountReapedChild(const Process *child, Time::Timestamp &user, Time::Timestamp &kernel)
void drainDeferredTimeAccounting()
bool m_bExternalLeaseAdmissionClosed
void removeThread(Thread *pThread)
static Process * getInit()
bool takePendingChildTransition(bool includeStopped, bool includeContinued, ChildTransition &transition)
bool m_bTerminationReapable
bool m_bSharedAddressSpace
User * getEffectiveUser() const
Atomic< size_t > m_NextTid
Thread * m_pReservedTerminalOwner
static Process * m_pInitProcess
void transferExecProcessSignals(Thread *pThread)
bool transitionState(ProcessState expected, ProcessState desired)
bool selectPendingChildTransition(bool includeStopped, bool includeContinued, bool consume, ChildTransition &transition)
WaitQueue m_ExternalLeaseWaiters
OperationBarrier m_DeferredThreadReaps
Spinlock m_ExternalLeaseLock
VirtualAddressSpace * m_pAddressSpace
bool m_bTerminationRendezvousStarted
void suspendIfContinuationEpoch(int stopSignal, size_t continuationEpoch)
OperationBarrier m_TimeAccountingReports
void releaseVforkAddressSpace()
void installTerminalOwner(Thread *owner)
MUST_USE_RESULT bool acquireThreadById(ThreadLease &lease, size_t id)
void suspend(int stopSignal=0)
WaitQueue m_ThreadJoinWaiters
MUST_USE_RESULT bool acquireThread(ThreadLease &lease, size_t n)
void transitionToTerminating()
bool waitUntilTerminationReapable()
Time::Timestamp getUserTime() const
Process * addressSpaceOwner()
bool m_bTerminationCleanupStarted
bool terminatingThreadReapable(Thread *pThread, bool &wakeOwner)
void prepareForDestruction()
TerminalOwnerReservation reserveTerminalOwner()
bool m_bTimeAccountingReportsEnabled
DeferredTimeAccounting m_DeferredTimeAccounting
void threadExiting(Thread *pThread)
Mutex m_FilesystemContextLock
void suspendInternal(int stopSignal, bool checkContinuationEpoch, size_t continuationEpoch)
ReaperClaim tryClaimReaper()
virtual int64_t getUserId() const
size_t getContinuationEpoch()
bool m_bTerminationSealed
WaitQueue m_SuspensionWaiters
bool m_bExternalLeaseReleaseInProgress
SharedPointer< ControllingTerminal > m_Ctty
ChildTransition m_PendingChildTransition
void finishTermination(bool notifyParent=false) NORETURN
void publishReaperClaim()
MemoryAllocator m_DynamicSpaceAllocator
MemoryAllocator m_SpaceAllocator
void releaseThreadLease(Thread *thread)
void setTimeAccountingReportInterest(size_t interest, bool enabled)
Thread * m_pTerminatingThread
void publishTerminationStatus(bool notifyParent)
WaitQueue m_ChildStateWaiters
bool m_bThreadJoinAdmissionClosed
size_t addThread(Thread *pThread)
static void setInit(Process *pProcess)
WaitQueue m_TerminationWaiters
Vector< Thread * > m_Threads
void enableTimeAccountingReports(size_t initialInterest=~size_t(0))
Spinlock m_TimeAccountingLock
SharedPointer< UserspacePid > m_UserspacePid
size_t m_ContinuationEpoch
static bool getInterrupts()
static ProcessorInformation & information()
static void switchAddressSpace(VirtualAddressSpace &AddressSpace)
static void setInterrupts(bool bEnable)
void swap(RangeList &other) noexcept
This class manages how processes and threads are scheduled across processors.
Process * getChildProcess(Process *pParent, size_t n)
static Scheduler & instance()
size_t reserveProcessId()
MUST_USE_RESULT bool acquireProcess(ProcessLease &lease, size_t n)
void addProcess(Process *pProcess)
void removeProcess(Process *pProcess)
static SharedPointer< T > tryAdopt(T *ptr)
static SharedPointer< T > tryAllocate(Args...)
bool acquire(bool recurse=false, bool safe=true)
virtual void preserveProcessSignalsForThreadExit(Thread *thread)
virtual bool kill(KillReason killReason, Thread *pThread=0)=0
virtual void threadExiting(Thread *pThread)
virtual void prepareThreadsForExec(Thread *owner)
virtual void acquire()
Acquire full mutual exclusion for all Subsystem resources.
virtual void threadRemoved(Thread *pThread)
virtual void threadException(Thread *pThread, ExceptionType eType, InterruptState *pState=nullptr, uintptr_t faultAddress=0, uintptr_t errorCode=0)
void wakeForDeliverableEvents()
@ Continue
No unwind necessary, carry on as normal.
@ TerminateThread
Exit only this thread during Process exit.
bool m_bThreadExitRequested
Time::Timestamp getUserTime() const
UnwindType getUnwindState()
bool m_bProcessExitParticipant
Process * getParent() const
class PerProcessorScheduler * getScheduler() const
bool beginExternalLease()
void closeExternalLeaseAdmissionAndDrain()
A vector / dynamic array.
static VirtualAddressSpace * create()
virtual void revertToKernelAddressSpace()=0
MUST_USE_RESULT WakeReason wait(const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0, StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
MUST_USE_RESULT WakeReason waitForCompletion(const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0)
Special wrapper object for Process.
void pushBack(const T &value)