The Pedigree Project 0.1
Process.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <config.h>
21
22#if THREADS
23
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"
48
49#include "modules/system/users/Group.h"
50#include "modules/system/users/User.h"
51#include "modules/system/vfs/File.h"
52
53namespace {
54SharedPointer<UserspacePidNamespace> g_DefaultUserspacePidNamespace;
55
56SharedPointer<UserspacePidNamespace> defaultUserspacePidNamespace() {
57 if (!g_DefaultUserspacePidNamespace) {
58 g_DefaultUserspacePidNamespace = SharedPointer<UserspacePidNamespace>::tryAllocate();
59 }
60 return g_DefaultUserspacePidNamespace;
61}
62} // namespace
63
64UserspacePid::UserspacePid(const SharedPointer<UserspacePidNamespace>& space) : m_Namespace(space) {
65 for (auto current = space; current; current = current->parent()) {
66 m_Ids.pushBack(current->allocate());
67 }
68}
69
70size_t UserspacePid::id(const UserspacePidNamespace* space) const {
71 if (!space) {
72 space = m_Namespace.get();
73 }
74 size_t index = 0;
75 for (auto current = m_Namespace; current; current = current->parent(), ++index) {
76 if (current.get() == space) {
77 return m_Ids[index];
78 }
79 }
80 return 0;
81}
82
83SharedPointer<UserspacePidNamespace> Process::rootPidNamespace() {
84 return defaultUserspacePidNamespace();
85}
86
87SharedPointer<UserspacePidNamespace> Process::pidNamespaceForChildren() {
89 return m_ChildrenPidNamespace ? m_ChildrenPidNamespace
91 : defaultUserspacePidNamespace();
92}
93
94bool Process::pidNamespaceReady() const {
95 if (!m_UserspacePid) {
96 return false;
97 }
98 for (auto space = m_UserspaceNamespace; space; space = space->parent()) {
99 if (space->dead()) {
100 return false;
101 }
102 }
103 return true;
104}
105
106bool Process::createPidNamespace() {
107 if (m_bPublished || m_Threads.count() || !pidNamespaceReady() ||
108 m_UserspaceNamespace->depth() >= 32) {
109 return false;
110 }
112 auto identity =
114 if (!identity) {
115 return false;
116 }
117 m_UserspaceNamespace = space;
119 return true;
120}
121
122bool Process::unsharePidNamespace() {
123 if (!pidNamespaceReady() || m_UserspaceNamespace->depth() >= 32) {
124 return false;
125 }
127 return unsharePidNamespace(space);
128}
129
130bool Process::unsharePidNamespace(const SharedPointer<UserspacePidNamespace>& prepared) {
131 if (!prepared || prepared->parent().get() != m_UserspaceNamespace.get() ||
132 prepared->depth() > 32 || prepared->dead()) {
133 return false;
134 }
136 if (m_ChildrenPidNamespace) {
137 return false;
138 }
139 m_ChildrenPidNamespace = prepared;
140 return true;
141}
142
143namespace {
144bool acquireNamespaceReaper(Process* process, Scheduler::ProcessLease& reaper) {
145 for (auto space = process->pidNamespace(); space; space = space->parent()) {
146 Process* candidate = space->init();
147 if (!space->dead() && candidate != process &&
148 Scheduler::instance().acquireProcess(reaper, candidate)) {
149 // The namespace does not own its init. Validate identity after pinning
150 // so a reused Process address cannot become the namespace's reaper.
151 if (reaper->pidNamespace().get() == space.get() && reaper->getUserspaceId() == 1 &&
152 !space->dead()) {
153 return true;
154 }
155 }
156 reaper.reset();
157 }
158 Process* candidate = Process::getInit();
159 if (candidate != process && Scheduler::instance().acquireProcess(reaper, candidate) &&
160 Process::getInit() == reaper.get()) {
161 return true;
162 }
163 reaper.reset();
164 return false;
165}
166} // namespace
167
169
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;
175#endif
176
177namespace {
178bool canAdoptChildren(Process* pProcess, Process* pExcluded = 0) {
179 if (!pProcess || pProcess == pExcluded) {
180 return false;
181 }
182
183 const Process::ProcessState state = pProcess->getState();
184 return state == Process::Active || state == Process::Suspended;
185}
186
187} // namespace
188
189Process::ExecScope::ExecScope(Process& process, bool active)
190 : m_pProcess(nullptr), m_bAdmitted(!active), m_TerminationDeferral(active) {
191 if (!active) {
192 return;
193 }
194 Thread* current = Processor::information().getCurrentThread();
195 LockGuard<Spinlock> guard(process.m_Lock);
196 if (current && current->getParent() == &process && !process.m_pExecOwner &&
197 process.getState() == Active && !process.m_bTerminalOwnerReserved &&
198 current->getUnwindState() == Thread::Continue) {
199 process.m_pExecOwner = current;
200 m_pProcess = &process;
201 m_bAdmitted = true;
202 }
203}
204
205Process::ExecScope::~ExecScope() {
206 if (!m_pProcess) {
207 return;
208 }
209 {
210 LockGuard<Spinlock> guard(m_pProcess->m_Lock);
211 if (m_pProcess->m_bExecCommitted) {
212 auto joinGuard = m_pProcess->m_ThreadJoinWaiters.acquire();
213 m_pProcess->m_bThreadJoinAdmissionClosed = false;
214 }
215 m_pProcess->m_bExecCommitted = false;
216 m_pProcess->m_bExecExitForwarded = false;
217 m_pProcess->m_pExecOwner = nullptr;
218 }
219 m_pProcess->m_ExecWaiters.wakeAll();
220}
221
222Process::ThreadCreationScope::ThreadCreationScope(Process& process)
223 : m_pProcess(nullptr), m_TerminationDeferral(true) {
224 LockGuard<Spinlock> guard(process.m_Lock);
225 if (!process.m_pExecOwner && process.getState() == Active && !process.m_bTerminalOwnerReserved) {
226 ++process.m_nThreadCreations;
227 m_pProcess = &process;
228 }
229}
230
231Process::ThreadCreationScope::~ThreadCreationScope() {
232 if (!m_pProcess) {
233 return;
234 }
235 {
236 LockGuard<Spinlock> guard(m_pProcess->m_Lock);
237 --m_pProcess->m_nThreadCreations;
238 }
239 m_pProcess->m_ExecWaiters.wakeAll();
240}
241
242bool Process::ExecScope::commit() {
243 if (!m_pProcess) {
244 return m_bAdmitted;
245 }
246 Process& process = *m_pProcess;
247 Thread* current = Processor::information().getCurrentThread();
248 Vector<Thread*> peers;
249 while (true) {
250 auto progress = process.m_ExecWaiters.acquire();
251 bool creatorsFinished = false;
252 {
253 LockGuard<Spinlock> guard(process.m_Lock);
254 if (process.getState() != Active || process.m_bTerminalOwnerReserved ||
255 current->getUnwindState() != Thread::Continue) {
256 return false;
257 }
258 creatorsFinished = process.m_nThreadCreations == 0;
259 if (creatorsFinished) {
260 // Retain peers before waking them. Detached retirement and kernel
261 // joins must not free a pointer in this teardown snapshot.
262 process.m_bExecCommitted = true;
263 for (Thread* thread : process.m_Threads) {
264 auto exitGuard = thread->m_JoinWaiters.acquire();
265 if (thread != current && thread->m_bReapable &&
266 (thread->m_bDetachedRetirementClaimed || thread->m_bJoinClaimed)) {
267 continue;
268 }
269 thread->m_bProcessExitOwned = true;
270 if (!thread->m_bReapable) {
271 thread->m_bProcessExitParticipant = true;
273 }
274 if (thread != current) {
275 peers.pushBack(thread);
276 }
277 }
278 auto joinGuard = process.m_ThreadJoinWaiters.acquire();
279 process.m_bThreadJoinAdmissionClosed = true;
280 }
281 }
282 if (creatorsFinished) {
283 break;
284 }
285 const WaitQueue::WakeReason reason = progress.waitForCompletion(
286 WaitQueue::Channel(), Thread::ProcessWait, reinterpret_cast<uintptr_t>(&process));
287 (void)reason;
288 }
289
290 if (process.m_pSubsystem) {
291 process.m_pSubsystem->prepareThreadsForExec(current);
292 }
293
294 for (Thread* peer : peers) {
295 peer->setUnwindState(Thread::TerminateThread);
296 }
297 while (true) {
298 auto progress = process.m_ExecWaiters.acquire();
299 bool peersOffStack = false;
300 {
301 LockGuard<Spinlock> guard(process.m_Lock);
302 peersOffStack = process.m_nTerminationParticipants == 1;
303 }
304 if (peersOffStack) {
305 break;
306 }
307 const WaitQueue::WakeReason reason = progress.waitForCompletion(
308 WaitQueue::Channel(), Thread::ProcessWait, reinterpret_cast<uintptr_t>(&process));
309 (void)reason;
310 }
311
312 // A join can outlive the target's final context switch. Drain its ownership
313 // decision before deleting retained threads or reopening join admission.
314 while (true) {
315 auto joins = process.m_ThreadJoinWaiters.acquire();
316 if (!process.m_nThreadJoinOperations) {
317 break;
318 }
319 const WaitQueue::WakeReason reason = joins.waitForCompletion(
320 WaitQueue::Channel(&process), Thread::ProcessWait, reinterpret_cast<uintptr_t>(&process));
321 (void)reason;
322 }
323 for (Thread* peer : peers) {
324 peer->closeExternalLeaseAdmissionAndDrain();
325 delete peer;
326 }
327 while (true) {
328 auto progress = process.m_ExecWaiters.acquire();
329 {
330 LockGuard<Spinlock> guard(process.m_Lock);
331 if (process.m_Threads.count() == 1) {
332 current->m_bProcessExitOwned = false;
333 current->m_bProcessExitParticipant = false;
334 process.m_nTerminationParticipants = 0;
335 return true;
336 }
337 }
338 // A detached deletion claimed before our snapshot owns its remaining
339 // TLS and stack cleanup; removeThread publishes its final completion.
340 const WaitQueue::WakeReason reason = progress.waitForCompletion(
341 WaitQueue::Channel(), Thread::ProcessWait, reinterpret_cast<uintptr_t>(&process));
342 (void)reason;
343 }
344}
345
346void Process::ExecScope::adoptLeaderIdentity() {
347 if (m_pProcess) {
348 LockGuard<Spinlock> guard(m_pProcess->m_Lock);
349 __atomic_store_n(&m_pProcess->m_pExecOwner->m_TaskId, m_pProcess->m_Id, __ATOMIC_RELEASE);
350 }
351}
352
353Process::ThreadLease::ThreadLease()
354 : m_pProcess(nullptr), m_pThread(nullptr), m_TerminationDeferral(false) {}
355
356Process::ReaperClaim::ReaperClaim() : m_pProcess(nullptr), m_TerminationDeferral(false) {}
357
358Process::ReaperClaim::ReaperClaim(Process* process)
359 : m_pProcess(process), m_TerminationDeferral(process != nullptr) {}
360
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;
365}
366
367Process::ReaperClaim::~ReaperClaim() {
368 if (m_pProcess) {
369 FATAL("Process reaper ownership left scope without publication");
370 }
371}
372
373Process::ReaperClaim& Process::ReaperClaim::operator=(ReaperClaim&& other) noexcept {
374 if (this != &other) {
375 if (m_pProcess) {
376 FATAL("Process reaper ownership overwritten before publication");
377 }
378 m_pProcess = other.m_pProcess;
379 m_TerminationDeferral = pedigree_std::move(other.m_TerminationDeferral);
380 other.m_pProcess = nullptr;
381 }
382 return *this;
383}
384
385void Process::ReaperClaim::publish() {
386 if (!m_pProcess) {
387 FATAL("Invalid Process reaper publication");
388 }
389
390 Process* process = m_pProcess;
391 m_pProcess = nullptr;
392 process->publishReaperClaim();
393 m_TerminationDeferral = TerminationDeferral(false);
394}
395
396Process::TerminalOwnerReservation::TerminalOwnerReservation()
397 : m_pProcess(nullptr), m_TerminationDeferral(false) {}
398
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;
404}
405
406Process::TerminalOwnerReservation::~TerminalOwnerReservation() {
407 if (m_pProcess) {
408 FATAL("Process terminal-owner reservation left scope without installation");
409 }
410}
411
412Process::TerminalOwnerReservation& Process::TerminalOwnerReservation::operator=(
413 TerminalOwnerReservation&& other) noexcept {
414 if (this != &other) {
415 if (m_pProcess) {
416 FATAL("Process terminal-owner reservation overwritten before installation");
417 }
418 m_pProcess = other.m_pProcess;
419 m_TerminationDeferral = pedigree_std::move(other.m_TerminationDeferral);
420 other.m_pProcess = nullptr;
421 }
422 return *this;
423}
424
426 if (!m_pProcess || !owner) {
427 FATAL("Invalid Process terminal-owner installation");
428 }
429
430 Process* process = m_pProcess;
431 process->installTerminalOwner(owner);
432 m_pProcess = nullptr;
433 m_TerminationDeferral = TerminationDeferral(false);
434}
435
436Process::ThreadLease::ThreadLease(Process* process, Thread* thread)
437 : m_pProcess(process), m_pThread(thread), m_TerminationDeferral(process && thread) {}
438
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;
445}
446
447Process::ThreadLease::~ThreadLease() {
448 reset();
449}
450
451Process::ThreadLease& Process::ThreadLease::operator=(ThreadLease&& other) noexcept {
452 if (this != &other) {
453 if (other.m_pProcess && other.m_pThread) {
454 m_TerminationDeferral = pedigree_std::move(other.m_TerminationDeferral);
455
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) {
463 previousProcess->releaseThreadLease(previousThread);
464 }
465 } else {
466 reset();
467 }
468 }
469 return *this;
470}
471
472void Process::ThreadLease::reset() {
473 Process* process = m_pProcess;
474 Thread* thread = m_pThread;
475 m_pProcess = nullptr;
476 m_pThread = nullptr;
477 if (process) {
478 process->releaseThreadLease(thread);
479 }
480 m_TerminationDeferral = TerminationDeferral(false);
481}
482
483Process::FileContextLease::FileContextLease()
484 : m_pFile(nullptr), m_TerminationDeferral(false), m_Context() {}
485
486Process::FileContextLease::~FileContextLease() = default;
487
488void Process::FileContextLease::reset() {
489 m_pFile = nullptr;
490 m_Context.reset();
491 m_TerminationDeferral = TerminationDeferral(false);
492}
493
497
498Process::Process(DeferredPublication, ProcessType type)
499 : m_Threads(),
500 m_NextTid(0),
501 m_Id(Scheduler::instance().reserveProcessId()),
502 m_UserspaceNamespace(),
503 m_UserspacePid(),
504 str(),
505 m_pParent(0),
506 m_pAddressSpace(&VirtualAddressSpace::getKernelAddressSpace()),
507 m_ExitStatus(0),
508 m_FilesystemContextLock(),
509 m_FilesystemContext(),
510 m_bFilesystemContextReady(true),
511 m_Ctty(),
512 m_SpaceAllocator(false),
513 m_DynamicSpaceAllocator(false),
514 m_UserReservationLock(false),
515 m_UserReservationGeneration(0),
516 m_pUser(0),
517 m_pGroup(0),
518 m_pEffectiveUser(0),
519 m_pEffectiveGroup(0),
520 m_pDynamicLinker(0),
521 m_pSubsystem(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(),
535 m_State(Active),
536 m_Type(type),
537 m_bDestroying(false),
538 m_bPublished(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),
549 m_Lock(false),
550 m_TimeAccountingLock(false),
551 m_Metadata(),
552 m_DeferredTimeAccounting(),
553 m_TimeAccountingReports(),
554 m_bTimeAccountingReportsEnabled(false),
555 m_TimeAccountingReportInterest(0),
556 m_bSharedAddressSpace(false) {
557 resetCounts();
558 m_Metadata.startTime = Time::getTimeNanoseconds();
559
560 resetUserReservations();
561 Metrics::increment(Metrics::ProcessCreated);
562}
563
564Process::Process(Process* pParent, bool bCopyOnWrite)
565 : Process(DeferredPublication(), pParent, bCopyOnWrite) {
566 publish();
567}
568
569Process::Process(DeferredPublication, Process* pParent, bool bCopyOnWrite,
570 FilesystemContextMode filesystemContext, bool emptyAddressSpace, ProcessType type)
571 : m_Threads(),
572 m_NextTid(0),
573 m_Id(Scheduler::instance().reserveProcessId()),
574 m_UserspaceNamespace(pParent->pidNamespaceForChildren()),
575 m_UserspacePid(SharedPointer<UserspacePid>::tryAllocate(m_UserspaceNamespace)),
576 str(),
577 m_pParent(pParent),
578 m_pAddressSpace(0),
579 m_ExitStatus(0),
580 m_FilesystemContextLock(),
581 m_FilesystemContext(),
582 m_bFilesystemContextReady(true),
583 m_Ctty(),
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),
593 m_pSubsystem(0),
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(),
607 m_State(Active),
608 m_Type(type),
609 m_bDestroying(false),
610 m_bPublished(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),
621 m_Lock(false),
622 m_TimeAccountingLock(false),
623 m_Metadata(),
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");
632 }
633 m_SpaceAllocator.swap(inheritedReservations.normal);
634 m_DynamicSpaceAllocator.swap(inheritedReservations.dynamic);
635
636 {
637 TerminationDeferral filesystemContextDeferral;
639 m_Ctty = pParent->m_Ctty;
640 }
641 if (filesystemContext == FilesystemContextMode::Inherit) {
642 auto parentContext = pParent->acquireFilesystemContext();
643 if (parentContext)
644 m_bFilesystemContextReady = parentContext->forkForProcess(m_FilesystemContext);
645 } else {
646 m_bFilesystemContextReady = false;
647 }
648
649 // Resource counters describe the inherited address space, but forked CPU
650 // time and process age start at zero for the child. Individual atomic
651 // loads avoid racing a whole-struct copy with the running parent.
652 m_Metadata.heapUsage = pParent->getHeapUsage();
653 m_Metadata.virtualPages = pParent->getVirtualPageCount();
654 m_Metadata.physicalPages = pParent->getPhysicalPageCount();
655 m_Metadata.sharedPages = pParent->getSharedPageCount();
656 m_Metadata.startTime = Time::getTimeNanoseconds();
657
658#if PEDIGREE_BENCHMARK_SYSCALL_TIMING
659 m_BenchmarkSyscallTiming = __atomic_load_n(&pParent->m_BenchmarkSyscallTiming, __ATOMIC_ACQUIRE);
660#endif
661#if PEDIGREE_BENCHMARK_SYSCALL_TRACE
662 m_BenchmarkSyscallTrace = __atomic_load_n(&pParent->m_BenchmarkSyscallTrace, __ATOMIC_ACQUIRE);
663#endif
664#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
665 m_BenchmarkVmDiagnostics = __atomic_load_n(&pParent->m_BenchmarkVmDiagnostics, __ATOMIC_ACQUIRE);
666#endif
667
668 m_pAddressSpace = emptyAddressSpace ? VirtualAddressSpace::create()
669 : pParent->m_pAddressSpace->clone(bCopyOnWrite);
670 if (emptyAddressSpace && m_pAddressSpace)
671 resetUserReservations();
672 str = pParent->str;
673
674 // Annotate the temporary description.
676 str += "<C>"; // C for cloned (i.e. shared address space)
677 } else {
678 str += "<F>"; // F for forked.
679 }
680 Metrics::increment(Metrics::ProcessCreated);
681}
682
683void Process::VforkCompletion::wait() {
684 Uninterruptible events;
685 TerminationDeferral termination;
686 for (;;) {
687 auto guard = m_Waiters.acquire();
688 if (m_Complete)
689 return;
690 const auto reason = guard.waitForCompletion(WaitQueue::Channel(), Thread::ProcessWait,
691 reinterpret_cast<uintptr_t>(this));
692 (void)reason;
693 }
694}
695
696void Process::VforkCompletion::complete() {
697 auto guard = m_Waiters.acquire();
698 m_Complete = true;
699 guard.wakeAll();
700}
701
702void Process::borrowVforkAddressSpace(Process& parent,
703 const SharedPointer<VforkCompletion>& completion) {
704 assert(!m_bPublished && !m_pVforkOwner && m_pAddressSpace && completion);
705 m_pVforkPrivateAddressSpace = m_pAddressSpace;
706 m_pVforkOwner = parent.addressSpaceOwner();
708 m_VforkCompletion = completion;
709}
710
712 if (!m_pVforkOwner)
713 return;
714 const bool interrupts = Processor::getInterrupts();
716 m_pAddressSpace = m_pVforkPrivateAddressSpace;
717 m_pVforkPrivateAddressSpace = nullptr;
718 m_pVforkOwner = nullptr;
719 Thread* current = Processor::information().getCurrentThread();
720 if (current && current->getParent() == this)
722 Processor::setInterrupts(interrupts);
723 // The child can be reaped before its creator runs, so the waiter owns an
724 // independent reference. No CPU may still execute this child in the old VM.
725 auto completion = pedigree_std::move(m_VforkCompletion);
726 completion->complete();
727}
728
729FilesystemContextRef Process::acquireFilesystemContext() const {
731 return m_FilesystemContext.reference();
732}
733
735 if (!context)
736 return false;
738 if (m_FilesystemContext)
739 return false;
740 // The old slot is empty, so moving ownership cannot invoke provider code.
741 m_FilesystemContext = pedigree_std::move(context);
742 m_bFilesystemContextReady = true;
743 return true;
744}
745
746bool Process::replaceFilesystemContext(FilesystemContextOwner&& replacement,
747 const FilesystemContextRef& expected) {
748 if (!replacement) {
749 return false;
750 }
752 {
754 if (m_FilesystemContext.reference().get() != expected.get()) {
755 return false;
756 }
757 retired = pedigree_std::move(m_FilesystemContext);
758 m_FilesystemContext = pedigree_std::move(replacement);
759 }
760 retired.reset();
761 return true;
762}
763
764bool Process::filesystemContextReady() const {
766 return m_bFilesystemContextReady;
767}
768
769void Process::releaseFilesystemContext() {
771 {
773 retired = pedigree_std::move(m_FilesystemContext);
774 m_bFilesystemContextReady = false;
775 }
776 retired.reset();
777}
778
779namespace {
780class CoreControllingTerminal : public Process::ControllingTerminal {
781 public:
782 explicit CoreControllingTerminal(File* file) : m_File(file) {}
783 ~CoreControllingTerminal() override {
784 m_File->releaseVfsReference();
785 }
786 File* file() const override {
787 return m_File;
788 }
789
790 private:
791 File* m_File;
792};
793} // namespace
794
795SharedPointer<Process::ControllingTerminal> Process::acquireCttyContext() const {
797 return m_Ctty;
798}
799
800File* Process::acquireCtty(FileContextLease& lease) const {
801 lease.m_TerminationDeferral = TerminationDeferral(true);
802 lease.m_Context = acquireCttyContext();
803 lease.m_pFile = lease.m_Context ? lease.m_Context->file() : nullptr;
804 if (!lease.m_pFile) {
805 lease.reset();
806 }
807 return lease.get();
808}
809
810void Process::setCttyContext(const SharedPointer<ControllingTerminal>& context) {
811 TerminationDeferral deferral;
813 {
815 retired = pedigree_std::move(m_Ctty);
816 m_Ctty = context;
817 }
818}
819
821 if (!file) {
822 setCttyContext(SharedPointer<ControllingTerminal>());
823 return true;
824 }
825 if (!file->retainVfsReference())
826 return false;
827 auto* raw = new CoreControllingTerminal(file);
828 if (!raw) {
829 file->releaseVfsReference();
830 return false;
831 }
833 if (!context)
834 return false;
835 setCttyContext(context);
836 return true;
837}
838
839void Process::enableTimeAccountingReports(size_t initialInterest) {
840 __atomic_store_n(&m_TimeAccountingReportInterest, initialInterest, __ATOMIC_RELEASE);
841 __atomic_store_n(&m_bTimeAccountingReportsEnabled, true, __ATOMIC_RELEASE);
842}
843
844void Process::setTimeAccountingReportInterest(size_t interest, bool enabled) {
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);
848 // Catch CPU time published between a timer's baseline snapshot and arming,
849 // even if no later mode transition publishes another batch.
850 if (enabled && interest && !previous &&
851 __atomic_load_n(&m_bTimeAccountingReportsEnabled, __ATOMIC_ACQUIRE) &&
853 Processor::information().getScheduler().publishDeferredTimeAccounting();
854 }
855}
856
857void Process::accountReapedChild(const Process* child, Time::Timestamp& user,
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");
861 }
862
863 user = child->getUserTime() + child->getReapedChildrenUserTime();
864 kernel = child->getKernelTime() + child->getReapedChildrenKernelTime();
865 __atomic_fetch_add(&m_Metadata.reapedChildrenUserTime, user, __ATOMIC_RELAXED);
866 __atomic_fetch_add(&m_Metadata.reapedChildrenKernelTime, kernel, __ATOMIC_RELAXED);
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],
874 __ATOMIC_RELAXED);
875 }
876#endif
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);
884 }
885#endif
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);
890 }
891#endif
892}
893
894void Process::queueTimeAccountingReport(Time::Timestamp elapsed) {
895 if (m_DeferredTimeAccounting.publish(elapsed)) {
896 Processor::information().getScheduler().publishDeferredTimeAccounting();
897 }
898}
899
900Time::Timestamp Process::totalTime(CpuTimeMode mode) const {
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);
905 if (!m_bDestroying) {
906 for (Thread* thread : m_Threads) {
907 total += mode == CpuTimeMode::User ? thread->getUserTime() : thread->getKernelTime();
908 }
909 }
910 return total;
911}
912
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);
918}
919
920void Process::closeTimeAccountingForHostedTest() {
922}
923#endif
924
927 if (!m_TimeAccountingReports.tryAcquire(report)) {
929 return;
930 }
931
933 const Time::Timestamp user = getUserTime();
934 reportTimesUpdated(user, user + getKernelTime());
935 }
936}
937
943
945 if (!filesystemContextReady())
946 FATAL("Process published before filesystem-context preparation completed");
947 if (m_bPublished) {
948 FATAL("Process::publish() called more than once.");
949 }
950
951 if (m_UserspaceNamespace && getUserspaceId() == 1) {
952 m_UserspaceNamespace->setInit(this);
953 }
954
955 Process* pRequestedParent = getParent();
956 if (!pRequestedParent) {
958 m_bPublished = true;
959 return;
960 }
961
962 Scheduler::ProcessLease requestedParent;
963 if (!Scheduler::instance().acquireProcess(requestedParent, pRequestedParent)) {
965 const bool initAcquired = acquireNamespaceReaper(this, init);
966 Process* publishParent =
967 initAcquired && canAdoptChildren(init.get(), this) ? init.get() : nullptr;
968 if (publishParent) {
969 auto publishGuard = publishParent->m_ChildStateWaiters.acquire();
970 if (canAdoptChildren(publishParent, this)) {
971 __atomic_store_n(&m_pParent, publishParent, __ATOMIC_RELEASE);
973 m_bPublished = true;
974 publishGuard.wakeAll();
975 return;
976 }
977 }
978 __atomic_store_n(&m_pParent, static_cast<Process*>(nullptr), __ATOMIC_RELEASE);
980 m_bPublished = true;
981 return;
982 }
983
984 // Publication and parent teardown use the same child-state guard. If the
985 // requested parent has already begun exiting, publish under a live init or
986 // as an orphan instead of appearing after the parent's reparenting scan.
987 auto parentGuard = requestedParent->m_ChildStateWaiters.acquire();
988 Process* pPublishParent = pRequestedParent;
989 if (!canAdoptChildren(pPublishParent)) {
990 pPublishParent = nullptr;
991 }
992
993 if (!pPublishParent) {
994 Scheduler::ProcessLease publishParent;
995 const bool publishParentAcquired = acquireNamespaceReaper(this, publishParent);
996 if (publishParentAcquired && publishParent.get() != pRequestedParent &&
997 canAdoptChildren(publishParent.get(), this)) {
998 auto publishGuard = publishParent->m_ChildStateWaiters.acquire();
999 pPublishParent = publishParent.get();
1000 __atomic_store_n(&m_pParent, pPublishParent, __ATOMIC_RELEASE);
1002 m_bPublished = true;
1003 parentGuard.wakeAll();
1004 publishGuard.wakeAll();
1005 return;
1006 }
1007 }
1008
1009 if (!canAdoptChildren(pPublishParent, this)) {
1010 pPublishParent = 0;
1011 }
1012 __atomic_store_n(&m_pParent, pPublishParent, __ATOMIC_RELEASE);
1014 m_bPublished = true;
1015 parentGuard.wakeAll();
1016}
1017
1018#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1019void Process::makeOrphanBeforePublicationForHostedTest() {
1020 if (m_bPublished) {
1021 FATAL(
1022 "Process test fixture attempted to become an orphan after "
1023 "publication.");
1024 }
1025 __atomic_store_n(&m_pParent, static_cast<Process*>(nullptr), __ATOMIC_RELEASE);
1026}
1027#endif
1028
1030 // A dying process does not receive a fresh virtual/profile timer signal.
1031 // Close worker admission, let an already-running report leave scope, and
1032 // discard time published by peers while their termination was in flight.
1034
1035 Process* expectedInit = this;
1036 __atomic_compare_exchange_n(&m_pInitProcess, &expectedInit, static_cast<Process*>(0), false,
1037 __ATOMIC_RELEASE, __ATOMIC_RELAXED);
1038
1039 if (!m_bPublished || m_bUnregistered) {
1042 return;
1043 }
1044
1045 // Remove ourselves from enumeration while holding the same parent-owned
1046 // guard waitpid uses to inspect child pointers. Once this releases, a new
1047 // scan cannot discover this object and an existing scan has finished.
1048 bool unregistered = false;
1049 while (!unregistered) {
1050 Process* pParent = getParent();
1051 if (pParent) {
1053 if (!Scheduler::instance().acquireProcess(parent, pParent)) {
1054 if (getParent() != pParent) {
1055 continue;
1056 }
1057 FATAL(
1058 "Process retained an unpinned parent during "
1059 "destruction.");
1060 }
1061 auto guard = parent->m_ChildStateWaiters.acquire();
1062 if (getParent() != pParent) {
1063 continue;
1064 }
1065
1067 m_bUnregistered = true;
1068 guard.wakeAll();
1069 unregistered = true;
1070 } else {
1072 m_bUnregistered = true;
1073 unregistered = true;
1074 }
1075 }
1076
1078}
1079
1081 TerminationDeferral terminationDeferral;
1083
1084 // A scheduler callback can publish a reapable detached Thread immediately
1085 // before process termination observes it. Keep the Process and its address
1086 // space alive until the ordinary retirement worker has removed that Thread.
1088
1089 // A joiner releases this lease only after it has stopped using both the
1090 // target Thread and this Process. Closing admission first makes the drain
1091 // a one-way lifetime barrier.
1092 while (true) {
1093 auto joinGuard = m_ThreadJoinWaiters.acquire();
1096 break;
1097 }
1098
1099 const WaitQueue::WakeReason reason = joinGuard.waitForCompletion(
1100 WaitQueue::Channel(this), Thread::ProcessWait, reinterpret_cast<uintptr_t>(this));
1101 (void)reason;
1102 }
1103
1104 // Make sure we have full mutual exclusion on the Subsystem before we lock
1105 // here. This ensures we have full access to the subsystem and avoids a case
1106 // where we lock here but the subsystem destruction needs to reschedule to
1107 // acquire the subsystem locks.
1108 if (m_pSubsystem) {
1110 }
1111
1112 // Close topology mutation while holding the vector lock, then drop the
1113 // spinlock before any lease drain or destructor can sleep. A remover that
1114 // entered earlier finishes before this transition; later add/remove calls
1115 // observe m_bDestroying and cannot mutate m_Threads.
1116 {
1119 // No accounting writer survives the off-stack barrier. Preserve all
1120 // totals before destruction starts leaving stale pointers in the vector.
1121 for (Thread* thread : m_Threads) {
1122 __atomic_fetch_add(&m_Metadata.userTime, thread->getUserTime(), __ATOMIC_RELAXED);
1123 __atomic_fetch_add(&m_Metadata.kernelTime, thread->getKernelTime(), __ATOMIC_RELAXED);
1124 }
1125 m_bDestroying = true;
1126 }
1127
1128 for (Vector<Thread*>::Iterator it = m_Threads.begin(); it != m_Threads.end(); ++it) {
1129 Thread* pThread = *it;
1131 auto threadExitGuard = pThread->m_JoinWaiters.acquire();
1132 if (!pThread->m_bReapable) {
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.");
1136 }
1137 if (pThread->m_bJoinClaimed) {
1138 FATAL("Process::~Process invariant failed for pid "
1139 << Dec << m_Id << ", tid " << pThread->getId()
1140 << ": a join claim escaped the lifetime barrier.");
1141 }
1142 if (pThread == Processor::information().getCurrentThread()) {
1143 FATAL("Process::~Process invariant failed for pid "
1144 << Dec << m_Id << ": destructor is running on a thread stack it would free.");
1145 }
1146 }
1147
1148 // Reapable threads have no live stack users. Process destruction owns any
1149 // objects retained for join, including process-exit participants.
1150 for (Vector<Thread*>::Iterator it = m_Threads.begin(); it != m_Threads.end(); ++it) {
1151 delete *it;
1152 }
1153
1154 if (m_pSubsystem)
1155 delete m_pSubsystem;
1156
1157 releaseFilesystemContext();
1158
1159 VirtualAddressSpace& VAddressSpace = Processor::information().getVirtualAddressSpace();
1160
1161 bool bInterrupts = Processor::getInterrupts();
1163
1164 if (m_pAddressSpace) {
1167 Processor::switchAddressSpace(VAddressSpace);
1168 }
1169
1170 delete m_pAddressSpace;
1171
1172 str.append("<Z>");
1173
1174 Processor::setInterrupts(bInterrupts);
1175 Metrics::increment(Metrics::ProcessDestroyed);
1176}
1177
1179 auto guard = m_ThreadJoinWaiters.acquire();
1181 return false;
1182 }
1183
1185 return true;
1186}
1187
1189 auto guard = m_ThreadJoinWaiters.acquire();
1191 FATAL("Process::endThreadJoin underflow for pid " << Dec << m_Id << ".");
1192 }
1193
1196 guard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(this));
1197 }
1198}
1199
1203 return false;
1204 }
1205
1207 return true;
1208}
1209
1211 bool wake = false;
1212 bool finalRelease = false;
1213 bool finishClosedRelease = false;
1214 {
1216 if (!m_nExternalLeases) {
1217 FATAL("Process external lease underflow for pid " << Dec << m_Id << ".");
1218 }
1219
1221 finalRelease = !m_nExternalLeases;
1222 finishClosedRelease = finalRelease && m_bExternalLeaseAdmissionClosed;
1223 wake = finishClosedRelease;
1224 if (finishClosedRelease) {
1226 }
1227 }
1228
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);
1233 if (hook) {
1234 hook(this, ExternalLeaseFinalReleaseUnlocked);
1235 }
1236 }
1237#endif
1238
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);
1244 }
1245#endif
1246 if (wake) {
1247 m_ExternalLeaseWaiters.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(this));
1248 }
1249
1250 // A drainer closes admission under m_ExternalLeaseLock before testing
1251 // this count. An open final release therefore has nobody to wake, and
1252 // must not touch this Process after the predicate lock is released.
1253 return;
1254 }
1255
1256 auto waiterGuard = m_ExternalLeaseWaiters.acquire();
1257 {
1260 }
1261
1262 if (wake) {
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);
1267 }
1268#endif
1269 waiterGuard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(this));
1270 }
1271}
1272
1277
1279 TerminationDeferral terminationDeferral;
1280 while (true) {
1281 auto guard = m_ExternalLeaseWaiters.acquire();
1282 {
1286 return;
1287 }
1288 }
1289
1290 const WaitQueue::WakeReason reason = guard.waitForCompletion(
1291 WaitQueue::Channel(this), Thread::ProcessWait, reinterpret_cast<uintptr_t>(this));
1292 (void)reason;
1293 }
1294}
1295
1297 if (thread) {
1298 thread->endExternalLease();
1299 }
1301}
1302
1303size_t Process::addThread(Thread* pThread) {
1305 if (!pThread)
1306 return ~0;
1307 const ProcessState state = getState();
1308 if (m_bDestroying || m_bTerminationSealed || m_bExecCommitted || state == Terminated ||
1309 state == Reaped) {
1310 FATAL("Process::addThread invariant failed for pid "
1311 << Dec << m_Id << ": a thread cannot be published after exit rendezvous seals.");
1312 }
1313 if (state == Terminating) {
1314 // A create-vs-exit race joins the rendezvous but never runs its entry
1315 // point. The add worker retires this Created thread off-stack.
1316 pThread->m_bProcessExitOwned = true;
1317 pThread->m_bProcessExitParticipant = true;
1318 __atomic_store_n(&pThread->m_UnwindState, Thread::TerminateThread, __ATOMIC_RELEASE);
1320 }
1321 {
1323 m_Threads.pushBack(pThread);
1324 }
1325 const size_t localId = m_NextTid += 1;
1326 __atomic_store_n(&pThread->m_TaskId,
1327 localId == 1 ? m_Id : Scheduler::instance().reserveProcessId(),
1328 __ATOMIC_RELEASE);
1329 pThread->m_UserspacePid = localId == 1
1332 Metrics::increment(Metrics::ThreadCreated);
1333 return localId;
1334}
1335
1337 if (m_pSubsystem) {
1338 m_pSubsystem->threadExiting(pThread);
1339 }
1340}
1341
1343 // Final process exit holds m_Lock through its last scheduler handoff.
1344 // Signals have no surviving recipient once that terminal phase begins.
1345 const ProcessState state = getState();
1346 if (m_pSubsystem && (state == Active || state == Suspended)) {
1348 }
1349}
1350
1352 {
1354
1355 // The destructor owns its vector iteration and deliberately leaves removal
1356 // until the whole Process object disappears. Logical process termination,
1357 // however, can outlive detached Thread destruction while waitpid waits.
1358 if (m_bDestroying)
1359 return;
1360 {
1362 for (Vector<Thread*>::Iterator it = m_Threads.begin(); it != m_Threads.end(); it++) {
1363 if (*it == pThread) {
1364 // Readers hold the same lock, so removal cannot make a total vanish
1365 // or expose both the live and retired contribution.
1366 __atomic_fetch_add(&m_Metadata.userTime, pThread->getUserTime(), __ATOMIC_RELAXED);
1367 __atomic_fetch_add(&m_Metadata.kernelTime, pThread->getKernelTime(), __ATOMIC_RELAXED);
1368 m_Threads.erase(it);
1369 break;
1370 }
1371 }
1372 }
1373
1374 if (m_pSubsystem)
1375 m_pSubsystem->threadRemoved(pThread);
1376 }
1377 m_ExecWaiters.wakeAll();
1378}
1379
1382 return m_Threads.count();
1383}
1384
1385bool Process::acquireThread(ThreadLease& lease, size_t n) {
1386 Thread* thread = nullptr;
1387 {
1389 if (n < m_Threads.count() && beginExternalLease()) {
1390 thread = m_Threads[n];
1391 if (!thread || !thread->beginExternalLease()) {
1393 thread = nullptr;
1394 }
1395 }
1396 }
1397
1398 if (!thread) {
1399 lease.reset();
1400 return false;
1401 }
1402
1403 lease = ThreadLease(this, thread);
1404 return true;
1405}
1406
1408 Thread* target = nullptr;
1409 {
1411 const ProcessState state = getState();
1412 if ((state == Active || state == Suspended) && beginExternalLease()) {
1413 auto acquire = [&](Thread* thread) {
1414 if (!thread) {
1415 return false;
1416 }
1417 LockGuard<Spinlock> threadGuard(thread->m_Lock);
1418 if (thread->m_bShutdown || thread->m_Status == Thread::Zombie ||
1419 thread->getUnwindState() == Thread::TerminateThread || !thread->beginExternalLease()) {
1420 return false;
1421 }
1422 target = thread;
1423 return true;
1424 };
1425 if (!acquire(m_pExecOwner)) {
1426 for (Thread* thread : m_Threads) {
1427 if (acquire(thread)) {
1428 break;
1429 }
1430 }
1431 }
1432 if (!target) {
1434 }
1435 }
1436 }
1437
1438 if (!target) {
1439 lease.reset();
1440 return false;
1441 }
1442 lease = ThreadLease(this, target);
1443 return true;
1444}
1445
1447 Thread* thread = nullptr;
1448 {
1450 for (Vector<Thread*>::Iterator it = m_Threads.begin(); it != m_Threads.end(); ++it) {
1451 if (*it && (*it)->getId() == id) {
1452 thread = *it;
1453 break;
1454 }
1455 }
1456 if (!thread || !beginExternalLease()) {
1457 thread = nullptr;
1458 } else if (!thread->beginExternalLease()) {
1460 thread = nullptr;
1461 }
1462 }
1463
1464 if (!thread) {
1465 lease.reset();
1466 return false;
1467 }
1468
1469 lease = ThreadLease(this, thread);
1470 return true;
1471}
1472
1473bool Process::acquireThreadByTaskId(ThreadLease& lease, size_t id) {
1474 Thread* thread = nullptr;
1475 {
1477 for (Vector<Thread*>::Iterator it = m_Threads.begin(); it != m_Threads.end(); ++it) {
1478 if (*it && (*it)->getTaskId() == id) {
1479 thread = *it;
1480 break;
1481 }
1482 }
1483 if (!thread || !beginExternalLease()) {
1484 thread = nullptr;
1485 } else if (!thread->beginExternalLease()) {
1487 thread = nullptr;
1488 }
1489 }
1490
1491 if (!thread) {
1492 lease.reset();
1493 return false;
1494 }
1495
1496 lease = ThreadLease(this, thread);
1497 return true;
1498}
1499
1500bool Process::acquireThreadByUserspaceId(ThreadLease& lease, size_t id,
1501 const UserspacePidNamespace* space) {
1502 Thread* thread = nullptr;
1503 {
1505 for (Vector<Thread*>::Iterator it = m_Threads.begin(); it != m_Threads.end(); ++it) {
1506 if (*it && (*it)->getUserspaceTaskId(space) == id) {
1507 thread = *it;
1508 break;
1509 }
1510 }
1511 if (!thread || !beginExternalLease()) {
1512 thread = nullptr;
1513 } else if (!thread->beginExternalLease()) {
1515 thread = nullptr;
1516 }
1517 }
1518
1519 if (!thread) {
1520 lease.reset();
1521 return false;
1522 }
1523
1524 lease = ThreadLease(this, thread);
1525 return true;
1526}
1527
1529 if (!expected) {
1530 lease.reset();
1531 return false;
1532 }
1533
1534 Thread* thread = nullptr;
1535 {
1537 for (Vector<Thread*>::Iterator it = m_Threads.begin(); it != m_Threads.end(); ++it) {
1538 if (*it == expected) {
1539 thread = *it;
1540 break;
1541 }
1542 }
1543 if (!thread || !beginExternalLease()) {
1544 thread = nullptr;
1545 } else if (!thread->beginExternalLease()) {
1547 thread = nullptr;
1548 }
1549 }
1550
1551 if (!thread) {
1552 lease.reset();
1553 return false;
1554 }
1555
1556 lease = ThreadLease(this, thread);
1557 return true;
1558}
1559
1561 TerminalOwnerReservation reservation;
1562 while (true) {
1563 auto progress = m_ExecWaiters.acquire();
1564 m_Lock.acquire();
1565 if (m_bExecCommitted) {
1566 m_Lock.release();
1567 const WaitQueue::WakeReason reason = progress.waitForCompletion(
1568 WaitQueue::Channel(), Thread::ProcessWait, reinterpret_cast<uintptr_t>(this));
1569 (void)reason;
1570 continue;
1571 }
1572 const ProcessState state = getState();
1574 state == Terminating || state == Terminated || state == Reaped) {
1575 m_Lock.release();
1576 return reservation;
1577 }
1579 FATAL("Process terminal owner reserved more than once for pid " << Dec << m_Id << ".");
1580 }
1581
1583 m_pReservedTerminalOwner = nullptr;
1584 reservation.m_pProcess = this;
1585 m_Lock.release();
1586 break;
1587 }
1588 reservation.m_TerminationDeferral = TerminationDeferral(true);
1589 return reservation;
1590}
1591
1595 FATAL("Process terminal owner installed without a reservation for pid " << Dec << m_Id << ".");
1596 }
1597
1598 bool found = false;
1599 for (Vector<Thread*>::Iterator it = m_Threads.begin(); it != m_Threads.end(); ++it) {
1600 if (*it == owner) {
1601 found = true;
1602 break;
1603 }
1604 }
1605 if (!found) {
1606 FATAL("Process terminal owner was not published to pid " << Dec << m_Id << ".");
1607 }
1608
1609 {
1610 LockGuard<Spinlock> ownerGuard(owner->m_Lock);
1611 if (owner->m_Status != Thread::Created || owner->m_bStartRequested ||
1612 owner->getUnwindState() != Thread::Continue) {
1613 FATAL("Process terminal owner was not installed before startup for pid " << Dec << m_Id
1614 << ".");
1615 }
1616 }
1617
1619}
1620
1622 return __atomic_compare_exchange_n(&m_State, &expected, desired, false, __ATOMIC_ACQ_REL,
1623 __ATOMIC_ACQUIRE);
1624}
1625
1627 while (true) {
1628 const ProcessState state = getState();
1629 if (state == Terminating) {
1630 return;
1631 }
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 << ".");
1635 }
1636 if (transitionState(state, Terminating)) {
1637 return;
1638 }
1639 }
1640}
1641
1643 if (!transitionState(Terminated, Reaped) && getState() != Reaped) {
1644 FATAL("Process state cannot transition to Reaped from "
1645 << Dec << static_cast<size_t>(getState()) << " for pid " << m_Id << ".");
1646 }
1647}
1648
1649void Process::markTerminating() {
1652}
1653
1655 Thread* current = Processor::information().getCurrentThread();
1656 if (!current || current->getParent() != this) {
1657 FATAL("Thread exit intent must come from the current process.");
1658 }
1659
1661 current->m_bThreadExitRequested = true;
1662 if (m_bTerminalOwnerReserved || m_bTerminationRendezvousStarted || getState() == Terminating) {
1663 return false;
1664 }
1665
1666 for (Thread* thread : m_Threads) {
1667 if (thread == current || thread->m_bThreadExitRequested ||
1669 continue;
1670 }
1671 auto exitGuard = thread->m_JoinWaiters.acquire();
1672 if (!thread->m_bExitStarted && !thread->m_bReapable) {
1673 return false;
1674 }
1675 }
1676
1677 // Reserve the running owner before dropping the lock. Concurrent exits
1678 // cannot elect another owner, and late clone publication joins termination.
1680 m_pReservedTerminalOwner = current;
1682 return true;
1683}
1684
1686 // Do not hold m_Lock while an admitted accounting report drains: POSIX
1687 // signal publication may itself need to inspect this Process's threads.
1689
1690 m_Lock.acquire();
1691 Thread* pCurrentThread = Processor::information().getCurrentThread();
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.");
1695 }
1696
1697 // Election and exec commit share m_Lock: neither may begin a peer
1698 // rendezvous between this check and publication of its ownership.
1699 if (m_bExecCommitted && m_pExecOwner != pCurrentThread) {
1700 if (m_bExecExitForwarded) {
1701 m_Lock.release();
1702 return false;
1703 }
1704 m_bExecExitForwarded = true;
1705 Thread* execOwner = m_pExecOwner;
1706 if (!beginExternalLease() || !execOwner->beginExternalLease()) {
1707 FATAL("Exec owner lost its lifetime during exit forwarding.");
1708 }
1709 m_Lock.release();
1710 ThreadLease owner(this, execOwner);
1711 if (cause == Subsystem::ExitCause::Signal) {
1712 owner->deferSignalExit(code);
1713 } else {
1714 owner->deferProcessExit(code);
1715 }
1716 return false;
1717 }
1718
1720 if (pCurrentThread != m_pReservedTerminalOwner) {
1721 m_Lock.release();
1722 return false;
1723 }
1725 m_pReservedTerminalOwner = nullptr;
1726 }
1727
1729 const bool isOwner = m_pTerminatingThread == pCurrentThread;
1730 if (!isOwner && !pCurrentThread->m_bProcessExitParticipant && !pCurrentThread->m_bReapable) {
1731 FATAL(
1732 "Process exit competitor was absent from the rendezvous for "
1733 "pid "
1734 << Dec << m_Id << ", tid " << pCurrentThread->getId() << ".");
1735 }
1736 m_Lock.release();
1737 return isOwner;
1738 }
1740 FATAL(
1741 "Process::beginTermination observed an invalid completed "
1742 "rendezvous for pid "
1743 << Dec << m_Id << ".");
1744 }
1746
1747 Vector<Thread*> peersToTerminate;
1748 bool currentThreadParticipates = false;
1749 for (Vector<Thread*>::Iterator it = m_Threads.begin(); it != m_Threads.end(); ++it) {
1750 Thread* pThread = *it;
1751 auto threadExitGuard = pThread->m_JoinWaiters.acquire();
1752 if (pThread->m_bReapable) {
1753 continue;
1754 }
1755 if (pThread->m_bProcessExitOwned != pThread->m_bProcessExitParticipant) {
1756 FATAL("Process::beginTermination invariant failed for pid "
1757 << Dec << m_Id << ", tid " << pThread->getId()
1758 << ": thread has inconsistent process-exit ownership.");
1759 }
1760
1761 if (!pThread->m_bProcessExitParticipant) {
1762 pThread->m_bProcessExitOwned = true;
1763 pThread->m_bProcessExitParticipant = true;
1765 }
1766
1767 if (pThread == pCurrentThread) {
1768 currentThreadParticipates = true;
1769 } else if (!pThread->m_bExitStarted) {
1770 peersToTerminate.pushBack(pThread);
1771 }
1772 }
1773
1774 if (!currentThreadParticipates) {
1775 FATAL("Process::beginTermination invariant failed for pid "
1776 << Dec << m_Id << ": initiating thread is absent or already off-stack.");
1777 }
1778
1779 {
1780 auto completionGuard = m_TerminationWaiters.acquire();
1781 m_pTerminatingThread = pCurrentThread;
1782 m_bTerminationReapable = false;
1783 }
1785
1786 const bool closesPidNamespace =
1788 if (closesPidNamespace) {
1789 m_UserspaceNamespace->close();
1790 }
1791
1792 // Init cannot accept any more orphans once its own teardown starts.
1793 Process* expectedInit = this;
1794 __atomic_compare_exchange_n(&m_pInitProcess, &expectedInit, static_cast<Process*>(0), false,
1795 __ATOMIC_RELEASE, __ATOMIC_RELAXED);
1796
1797#if VERBOSE_KERNEL
1798 Scheduler::ProcessLease logParent;
1799 if (Scheduler::instance().acquireProcess(logParent, getParent()))
1800 NOTICE("Kill: " << m_Id << " (parent: " << logParent->getId() << ")");
1801 else
1802 NOTICE("Kill: " << m_Id << " (parent: <orphan>)");
1803#endif
1804
1805 // Reparent every child, including terminated-but-unreaped children. Direct
1806 // deletion here races both waitpid and a child still switching off-stack.
1807 Scheduler::ProcessLease newParent;
1808 const bool newParentAcquired = acquireNamespaceReaper(this, newParent);
1809 Process* pNewParent = newParentAcquired ? newParent.get() : nullptr;
1810 auto moveChildren = [this](Process* pParent) {
1811 while (true) {
1812 Process* pChild = Scheduler::instance().getChildProcess(this, 0);
1813 if (!pChild) {
1814 break;
1815 }
1816
1817 __atomic_store_n(&pChild->m_pParent, pParent, __ATOMIC_RELEASE);
1818 }
1819 };
1820
1821 {
1822 auto oldParentGuard = m_ChildStateWaiters.acquire();
1823 if (pNewParent) {
1824 auto newParentGuard = pNewParent->m_ChildStateWaiters.acquire();
1825 if (!canAdoptChildren(pNewParent, this)) {
1826 pNewParent = 0;
1827 }
1828 moveChildren(pNewParent);
1829 oldParentGuard.wakeAll();
1830 newParentGuard.wakeAll();
1831 } else {
1832 moveChildren(0);
1833 oldParentGuard.wakeAll();
1834 }
1835 }
1836
1837 // Peer wakeup can make another same-core thread runnable immediately.
1838 // The current thread remains a rendezvous participant, so dropping m_Lock
1839 // here cannot make Process destruction observable.
1840 m_Lock.release();
1841
1842#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1843 if (m_TerminationElectionHook) {
1844 m_TerminationElectionHook(this, pCurrentThread);
1845 }
1846#endif
1847
1848 if (closesPidNamespace) {
1849 size_t after = 0;
1851 while (Scheduler::instance().acquireNextProcess(member, after)) {
1852 after = member->getId();
1853 if (member.get() == this || !member->getUserspaceId(m_UserspaceNamespace.get())) {
1854 continue;
1855 }
1856 ThreadLease recipient;
1857 if (member->getSubsystem() && member->acquireProcessSignalThread(recipient)) {
1858 member->getSubsystem()->kill(Subsystem::Unknown, recipient.get());
1859 }
1860 recipient.reset();
1862 }
1863 }
1864
1865 // Peers take a thread-only exit path. Re-entering Subsystem::exit() here
1866 // would rerun process teardown and recreate the historical deadlocks.
1867 for (Vector<Thread*>::Iterator it = peersToTerminate.begin(); it != peersToTerminate.end();
1868 ++it) {
1869 (*it)->setUnwindState(Thread::TerminateThread);
1870 }
1871
1872 return true;
1873}
1874
1876 Thread* pCurrentThread = Processor::information().getCurrentThread();
1877 {
1879 if (!m_bTerminationRendezvousStarted || m_pTerminatingThread != pCurrentThread) {
1880 return false;
1881 }
1883 return false;
1884 }
1885
1887 }
1888
1889 while (true) {
1890 auto progress = m_ExecWaiters.acquire();
1891 {
1893 // Admitted creators may still publish a Created peer into this
1894 // rendezvous. Terminating rejects new admissions; seal only after
1895 // the existing creation scopes have finished publication.
1896 if (m_nThreadCreations == 0) {
1897 m_bTerminationSealed = true;
1898 break;
1899 }
1900 }
1901 const WaitQueue::WakeReason reason = progress.waitForCompletion(
1902 WaitQueue::Channel(), Thread::ProcessWait, reinterpret_cast<uintptr_t>(this));
1903 (void)reason;
1904 }
1905
1906 while (true) {
1907 auto completionGuard = m_TerminationWaiters.acquire();
1908 m_Lock.acquire();
1909 const size_t participants = m_nTerminationParticipants;
1910 m_Lock.release();
1911 if (participants == 0) {
1912 FATAL("Process exit owner disappeared before teardown for pid " << Dec << m_Id << ".");
1913 }
1914 if (participants == 1) {
1915 return true;
1916 }
1917
1918 const WaitQueue::WakeReason reason =
1919 completionGuard.waitForCompletion(WaitQueue::Channel(), Thread::ProcessWait,
1920 reinterpret_cast<uintptr_t>(__builtin_return_address(0)));
1921 (void)reason;
1922 }
1923}
1924
1925void Process::finishTermination(bool notifyParent) {
1926 finishTermination(false, notifyParent);
1927}
1928
1929void Process::finishTermination(bool abandonStack, bool notifyParent) {
1930 Thread* pCurrentThread = Processor::information().getCurrentThread();
1931 {
1933 if (!m_bTerminationCleanupStarted || m_pTerminatingThread != pCurrentThread ||
1934 m_nTerminationParticipants != 1 || !pCurrentThread->m_bProcessExitParticipant) {
1935 FATAL(
1936 "Process::finishTermination called without exclusive "
1937 "off-stack teardown ownership for pid "
1938 << Dec << m_Id << ".");
1939 }
1940 }
1941
1942 // Derived cleanup may acquire blocking locks without coupling them to the
1943 // Process lock.
1945
1946 // Wait status must be visible before an ordinary parent notification can
1947 // dispatch a handler which immediately calls waitpid(WNOHANG).
1948 publishTerminationStatus(notifyParent);
1949
1950 // Add to the zombie queue if the process is an orphan.
1951 if (!getParent()) {
1952 NOTICE("Process::kill() - process is an orphan, adding to ZombieQueue.");
1953
1954 ReaperClaim reaper = tryClaimReaper();
1955 if (reaper) {
1956#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1957 OrphanPublicationHook publicationHook =
1958 __atomic_load_n(&m_OrphanPublicationHook, __ATOMIC_ACQUIRE);
1959 if (publicationHook) {
1960 publicationHook(this, OrphanPublicationPhase::Preparing, Processor::getInterrupts(),
1961 m_Lock.acquired());
1962 }
1963#endif
1964 reaper.publish();
1965#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1966 if (publicationHook) {
1967 publicationHook(this, OrphanPublicationPhase::Published, Processor::getInterrupts(),
1968 m_Lock.acquired());
1969 }
1970#endif
1971 }
1972 }
1973
1974 m_Lock.acquire();
1975 if (!m_bTerminationCleanupStarted || m_pTerminatingThread != pCurrentThread ||
1976 m_nTerminationParticipants != 1 || !pCurrentThread->m_bProcessExitParticipant) {
1977 FATAL(
1978 "Process::finishTermination lost exclusive off-stack teardown "
1979 "ownership for pid "
1980 << Dec << m_Id << ".");
1981 }
1982
1983 // Parent-owned processes are reaped by waitpid; orphan publication above
1984 // may already have blocked its worker on the off-stack completion.
1985#if VERBOSE_KERNEL
1986 if (getParent()) {
1987 NOTICE(
1988 "Process::kill() - not adding to ZombieQueue, process has a "
1989 "parent.");
1990 }
1991#endif
1992 if (abandonStack) {
1993 Processor::information().getScheduler().abandonCurrentThreadStack(
1994 PerProcessorScheduler::StackDiscardReason::EmergencyProcessKill, &m_Lock);
1995 }
1996 Processor::information().getScheduler().commitCurrentThreadExit(&m_Lock);
1997
1998 FATAL("Should never get here");
1999}
2000
2002 size_t expected = ReaperUnclaimed;
2003 if (!__atomic_compare_exchange_n(&m_ReaperState, &expected, ReaperClaimed, false,
2004 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
2005 return ReaperClaim();
2006 }
2007 return ReaperClaim(this);
2008}
2009
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");
2015 }
2016 ZombieQueue::instance().addObject(new ZombieProcess(this));
2017}
2018
2020 if (!beginTermination()) {
2021 Processor::information().getScheduler().abandonCurrentThreadStack(
2022 PerProcessorScheduler::StackDiscardReason::EmergencyProcessKill);
2023 }
2024 if (!quiesceTermination()) {
2025 FATAL("Process::kill failed to claim teardown for pid " << Dec << m_Id << ".");
2026 }
2027 finishTermination(true, false);
2028}
2029
2030#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
2031void Process::setTerminationElectionHook(TerminationElectionHook hook) {
2032 m_TerminationElectionHook = hook;
2033}
2034
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);
2039}
2040
2041bool Process::isTerminationReapableForHostedTest() {
2042 auto guard = m_TerminationWaiters.acquire();
2044}
2045
2046void Process::setOrphanPublicationHook(OrphanPublicationHook hook) {
2047 __atomic_store_n(&m_OrphanPublicationHook, hook, __ATOMIC_RELEASE);
2048}
2049#endif
2050
2051void Process::suspend(int stopSignal) {
2052 suspendInternal(stopSignal, false, 0);
2053}
2054
2055void Process::suspendIfContinuationEpoch(int stopSignal, size_t continuationEpoch) {
2056 suspendInternal(stopSignal, true, continuationEpoch);
2057}
2058
2059void Process::suspendInternal(int stopSignal, bool checkContinuationEpoch,
2060 size_t continuationEpoch) {
2061 bool published = false;
2062 bool enteredSuspended = false;
2063 while (!published) {
2064 Process* pParent = getParent();
2065 if (pParent) {
2067 if (!Scheduler::instance().acquireProcess(parent, pParent)) {
2068 if (getParent() != pParent) {
2069 continue;
2070 }
2071 FATAL("Process::suspend retained an unpinned parent.");
2072 }
2073 auto guard = parent->m_ChildStateWaiters.acquire();
2074 if (getParent() != pParent) {
2075 continue;
2076 }
2077
2078 auto suspensionGuard = m_SuspensionWaiters.acquire();
2079 const bool epochMatches = !checkContinuationEpoch || continuationEpoch == m_ContinuationEpoch;
2080 if (epochMatches && transitionState(Active, Suspended)) {
2081 m_PendingChildTransition.kind = ChildTransitionKind::Stopped;
2082 m_PendingChildTransition.stopSignal = stopSignal;
2083 enteredSuspended = true;
2084 guard.wakeAll();
2085 }
2086 published = true;
2087 } else {
2088 auto suspensionGuard = m_SuspensionWaiters.acquire();
2089 const bool epochMatches = !checkContinuationEpoch || continuationEpoch == m_ContinuationEpoch;
2090 if (epochMatches && transitionState(Active, Suspended)) {
2091 m_PendingChildTransition.kind = ChildTransitionKind::Stopped;
2092 m_PendingChildTransition.stopSignal = stopSignal;
2093 enteredSuspended = true;
2094 }
2095 published = true;
2096 }
2097 }
2098
2099 if (!enteredSuspended && getState() != Suspended) {
2100 return;
2101 }
2102
2103 // Notify the parent only for the thread that won the state transition.
2104 if (enteredSuspended) {
2106 if (Scheduler::instance().acquireProcess(parent, getParent()) && parent->getSubsystem()) {
2107 Process::ThreadLease parentThread;
2108 bool parentThreadAcquired = false;
2109 {
2110 auto relationGuard = parent->m_ChildStateWaiters.acquire();
2111 if (getParent() == parent.get()) {
2112 parentThreadAcquired = parent->acquireProcessSignalThread(parentThread);
2113 }
2114 }
2115 if (parentThreadAcquired) {
2116 parent->getSubsystem()->threadException(parentThread.get(), Subsystem::Child);
2117 }
2118 }
2119 }
2120
2121 Thread* current = Processor::information().getCurrentThread();
2122 if (current && current->getParent() == this) {
2123 // The stop-owning thread uses the same event-publication handshake as
2124 // return-bound peers. This retains a terminal event queued after the
2125 // Suspended transition but before the owner publishes its wait.
2126 if (current->getScheduler()->serviceProcessStopAtUserReturn()) {
2127 return;
2128 }
2129 return;
2130 }
2131
2132 // Kernel callers may synchronously suspend another Process. Preserve that
2133 // control-plane wait without applying the caller's own event policy.
2134 while (true) {
2135 auto guard = m_SuspensionWaiters.acquire();
2136 if (getState() != Suspended) {
2137 return;
2138 }
2139
2140 const WaitQueue::WakeReason wakeReason =
2141 guard.wait(WaitQueue::Channel(), Thread::ProcessWait,
2142 reinterpret_cast<uintptr_t>(__builtin_return_address(0)));
2143 if (wakeReason == WaitQueue::WakeReason::Terminating ||
2144 wakeReason == WaitQueue::WakeReason::Unwinding) {
2145 return;
2146 }
2147 }
2148}
2149
2151 auto suspensionGuard = m_SuspensionWaiters.acquire();
2152 return m_ContinuationEpoch;
2153}
2154
2156 bool published = false;
2157 bool resumed = false;
2158 while (!published) {
2159 Process* pParent = getParent();
2160 if (pParent) {
2162 if (!Scheduler::instance().acquireProcess(parent, pParent)) {
2163 if (getParent() != pParent) {
2164 continue;
2165 }
2166 FATAL("Process::resume retained an unpinned parent.");
2167 }
2168 auto guard = parent->m_ChildStateWaiters.acquire();
2169 if (getParent() != pParent) {
2170 continue;
2171 }
2172
2173 auto suspensionGuard = m_SuspensionWaiters.acquire();
2175 if (transitionState(Suspended, Active)) {
2176 m_PendingChildTransition.kind = ChildTransitionKind::Continued;
2177 m_PendingChildTransition.stopSignal = 0;
2178 resumed = true;
2179 suspensionGuard.wakeAll();
2180 guard.wakeAll();
2181 }
2182 published = true;
2183 } else {
2184 auto suspensionGuard = m_SuspensionWaiters.acquire();
2186 if (transitionState(Suspended, Active)) {
2187 m_PendingChildTransition.kind = ChildTransitionKind::Continued;
2188 m_PendingChildTransition.stopSignal = 0;
2189 resumed = true;
2190 suspensionGuard.wakeAll();
2191 }
2192 published = true;
2193 }
2194 }
2195
2196 if (!resumed) {
2197 return;
2198 }
2199
2200 // Wake return-boundary gates through the queue shared with event
2201 // publication, then recheck events that became eligible at Active.
2202 for (size_t i = getNumThreads(); i > 0; --i) {
2203 ThreadLease thread;
2204 if (acquireThread(thread, i - 1)) {
2205 {
2206 auto eventGuard = thread->m_EventWaiters.acquire();
2207 eventGuard.wakeAll(WaitQueue::WakeReason::Signalled,
2208 WaitQueue::Channel(this, static_cast<uintptr_t>(Thread::ProcessWait)));
2209 }
2210 thread->wakeForDeliverableEvents();
2211 }
2212 }
2213}
2214
2215bool Process::selectPendingChildTransition(bool includeStopped, bool includeContinued, bool consume,
2216 ChildTransition& transition) {
2217 auto suspensionGuard = m_SuspensionWaiters.acquire();
2218 const bool selected =
2219 (includeStopped && m_PendingChildTransition.kind == ChildTransitionKind::Stopped) ||
2220 (includeContinued && m_PendingChildTransition.kind == ChildTransitionKind::Continued);
2221 if (!selected) {
2222 transition = ChildTransition();
2223 return false;
2224 }
2225
2226 transition = m_PendingChildTransition;
2227 if (consume) {
2229 }
2230 return true;
2231}
2232
2233bool Process::takePendingChildTransition(bool includeStopped, bool includeContinued,
2234 ChildTransition& transition) {
2235 return selectPendingChildTransition(includeStopped, includeContinued, true, transition);
2236}
2237
2238int64_t Process::getUserId() const {
2239 User* identity = getUser();
2240 return identity ? static_cast<int64_t>(identity->getId()) : -1;
2241}
2242
2243int64_t Process::getGroupId() const {
2244 Group* identity = getGroup();
2245 return identity ? static_cast<int64_t>(identity->getId()) : -1;
2246}
2247
2248int64_t Process::getEffectiveUserId() const {
2250 return identity ? static_cast<int64_t>(identity->getId()) : -1;
2251}
2252
2253int64_t Process::getEffectiveGroupId() const {
2255 return identity ? static_cast<int64_t>(identity->getId()) : -1;
2256}
2257
2258void Process::getSupplementalGroupIds(Vector<int64_t>& vec) const {
2259 // no-op
2260}
2261
2262void Process::setUserId(int64_t) {}
2263
2264void Process::setGroupId(int64_t) {}
2265
2266void Process::setEffectiveUserId(int64_t) {}
2267
2268void Process::setEffectiveGroupId(int64_t) {}
2269
2271 while (true) {
2272 auto guard = m_TerminationWaiters.acquire();
2274 return true;
2275 }
2276 if (Processor::information().getCurrentThread() == m_pTerminatingThread) {
2277 return false;
2278 }
2279
2280 const WaitQueue::WakeReason wakeReason =
2281 guard.waitForCompletion(WaitQueue::Channel(), Thread::ProcessWait,
2282 reinterpret_cast<uintptr_t>(__builtin_return_address(0)));
2283 if (wakeReason == WaitQueue::WakeReason::Terminating ||
2284 wakeReason == WaitQueue::WakeReason::Unwinding) {
2285 // Reaper cancellation cannot make deleting a live Process safe.
2286 // Keep the wrapper alive until the off-stack completion arrives.
2287 continue;
2288 }
2289 }
2290}
2291
2293 while (true) {
2294 {
2295 auto guard = m_TerminationWaiters.acquire();
2297 return true;
2298 }
2299 if (Processor::information().getCurrentThread() == m_pTerminatingThread) {
2300 return false;
2301 }
2302 }
2303
2305 }
2306}
2307
2308bool Process::terminatingThreadReapable(Thread* pThread, bool& wakeOwner) {
2309 wakeOwner = false;
2310 if (!pThread->m_bProcessExitParticipant) {
2311 return false;
2312 }
2314 FATAL("Process exit rendezvous underflow for pid " << Dec << m_Id << ", tid "
2315 << pThread->getId() << ".");
2316 }
2317
2318 pThread->m_bProcessExitParticipant = false;
2321 m_bTerminationSealed = true;
2322 return true;
2323 }
2324
2325 wakeOwner = m_bTerminationRendezvousStarted || m_bExecCommitted;
2326 return false;
2327}
2328
2329void Process::publishTerminationStatus(bool notifyParent) {
2330 bool published = false;
2331 while (!published) {
2332 Process* pParent = getParent();
2333 if (pParent) {
2335 if (!Scheduler::instance().acquireProcess(parent, pParent)) {
2336 if (getParent() != pParent) {
2337 continue;
2338 }
2339 FATAL(
2340 "Process::publishTerminationStatus retained an unpinned "
2341 "parent.");
2342 }
2343
2344 // Pin the notification target before taking the child-state guard. Event
2345 // allocation and delivery happen after that guard is dropped.
2346 Process::ThreadLease parentThread;
2347 const bool parentThreadAcquired =
2348 notifyParent && parent->acquireProcessSignalThread(parentThread);
2349 bool parentAcceptsSignal = false;
2350 {
2351 auto guard = parent->m_ChildStateWaiters.acquire();
2352 if (getParent() != pParent) {
2353 continue;
2354 }
2355
2356 const ProcessState parentState = parent->getState();
2357 parentAcceptsSignal = parentState == Active || parentState == Suspended;
2358 {
2359 auto suspensionGuard = m_SuspensionWaiters.acquire();
2361 if (!transitionState(Terminating, Terminated)) {
2362 FATAL("Process state was not Terminating while publishing pid " << Dec << m_Id << ".");
2363 }
2364 Metrics::increment(Metrics::ProcessExited);
2365 }
2366 guard.wakeAll();
2367 published = true;
2368 }
2369
2370 if (parentThreadAcquired && parentAcceptsSignal && parent->getSubsystem()) {
2371 parent->getSubsystem()->threadException(parentThread.get(), Subsystem::Child);
2372 }
2373 } else {
2374 auto suspensionGuard = m_SuspensionWaiters.acquire();
2376 if (!transitionState(Terminating, Terminated)) {
2377 FATAL("Process state was not Terminating while publishing pid " << Dec << m_Id << ".");
2378 }
2379 Metrics::increment(Metrics::ProcessExited);
2380 published = true;
2381 }
2382 }
2383}
2384
2386 {
2389 FATAL("Process termination published with "
2390 << Dec << m_nTerminationParticipants << " live rendezvous participants for pid " << m_Id
2391 << ".");
2392 }
2393 for (Vector<Thread*>::Iterator it = m_Threads.begin(); it != m_Threads.end(); ++it) {
2394 Thread* pThread = *it;
2395 auto threadExitGuard = pThread->m_JoinWaiters.acquire();
2396 if (!pThread->m_bReapable) {
2397 FATAL("Process termination published before tid " << Dec << pThread->getId() << " of pid "
2398 << m_Id << " switched off-stack.");
2399 }
2400 }
2401 const ProcessState state = getState();
2402 if (state != Terminated && state != Reaped) {
2403 FATAL("Process off-stack completion observed unpublished wait status for pid " << Dec << m_Id
2404 << ".");
2405 }
2406 }
2407
2408 // This is deliberately the final Process access in the scheduler callback.
2409 // A ZombieQueue worker may begin destruction as soon as it observes this
2410 // completion.
2411 auto guard = m_TerminationWaiters.acquire();
2414 guard.wakeAll();
2415}
2416
2418 return __atomic_load_n(&m_pInitProcess, __ATOMIC_ACQUIRE);
2419}
2420
2421void Process::setInit(Process* pProcess) {
2422 Process* expected = 0;
2423 __atomic_compare_exchange_n(&m_pInitProcess, &expected, pProcess, false, __ATOMIC_RELEASE,
2424 __ATOMIC_RELAXED);
2425}
2426
2427#endif // THREADS
bool publish(Time::Timestamp elapsed)
Definition File.h:75
virtual bool retainVfsReference()
Definition File.cc:910
virtual void releaseVfsReference()
Definition File.cc:914
Definition Group.h:32
MUST_USE_RESULT bool tryAcquire(Lease &lease)
MUST_USE_RESULT bool serviceProcessStopAtUserReturn(ProcessStopGateMode mode=ProcessStopGateMode::StopOnly)
void install(Thread *owner)
Definition Process.cc:425
MUST_USE_RESULT bool setCtty(File *file)
Definition Process.cc:820
virtual void reportTimesUpdated(Time::Timestamp userTotal, Time::Timestamp total)
Definition Process.h:984
size_t getUserspaceId() const
Definition Process.h:504
Group * getEffectiveGroup() const
bool beginTermination(int code=0, Subsystem::ExitCause cause=Subsystem::ExitCause::Normal)
Definition Process.cc:1685
void drainExternalLeases()
Definition Process.cc:1278
void closeDeferredTimeAccounting()
Definition Process.cc:938
bool quiesceTermination()
Definition Process.cc:1875
void endExternalLease()
Definition Process.cc:1210
ProcessState
Definition Process.h:303
@ Reaped
Terminal wait status is visible; the owner may still be on-stack.
Definition Process.h:308
ssize_t getHeapUsage() const
Definition Process.h:902
MUST_USE_RESULT bool installFilesystemContext(FilesystemContextOwner &&context)
Definition Process.cc:734
bool m_bTerminalOwnerReserved
Definition Process.h:1180
bool waitUntilTerminationReapableForTerminalCoordinator()
Definition Process.cc:2292
Process * m_pParent
Definition Process.h:1033
size_t getId()
Definition Process.h:499
Subsystem * m_pSubsystem
Definition Process.h:1091
Thread * m_pExecOwner
Definition Process.h:1167
virtual void processTerminated()
Definition Process.h:1007
MUST_USE_RESULT bool acquireProcessSignalThread(ThreadLease &lease)
Definition Process.cc:1407
LargeStaticString str
Definition Process.h:1029
bool beginExternalLease()
Definition Process.cc:1200
Time::Timestamp totalTime(CpuTimeMode mode) const
Definition Process.cc:900
bool prepareThreadExit()
Definition Process.cc:1654
void endThreadJoin()
Definition Process.cc:1188
SharedPointer< UserspacePidNamespace > m_UserspaceNamespace
Definition Process.h:1022
void closeExternalLeaseAdmission()
Definition Process.cc:1273
Process * getParent()
Definition Process.h:620
bool m_bPublished
Definition Process.h:1158
void publishTerminationReapable()
Definition Process.cc:2385
bool m_bUnregistered
Definition Process.h:1161
VirtualAddressSpace * getAddressSpace()
Definition Process.h:530
size_t m_nTerminationParticipants
Definition Process.h:1174
size_t m_nThreadJoinOperations
Definition Process.h:1109
void accountReapedChild(const Process *child, Time::Timestamp &user, Time::Timestamp &kernel)
Definition Process.cc:857
void drainDeferredTimeAccounting()
Definition Process.cc:925
bool m_bExternalLeaseAdmissionClosed
Definition Process.h:1127
void removeThread(Thread *pThread)
Definition Process.cc:1351
Group * getGroup() const
static Process * getInit()
Definition Process.cc:2417
bool takePendingChildTransition(bool includeStopped, bool includeContinued, ChildTransition &transition)
Definition Process.cc:2233
bool m_bTerminationReapable
Definition Process.h:1192
bool m_bSharedAddressSpace
Definition Process.h:1331
User * getEffectiveUser() const
size_t m_ReaperState
Definition Process.h:1201
Atomic< size_t > m_NextTid
Definition Process.h:1016
Thread * m_pReservedTerminalOwner
Definition Process.h:1183
static Process * m_pInitProcess
Definition Process.h:1334
bool beginThreadJoin()
Definition Process.cc:1178
void reap()
Definition Process.cc:1642
size_t getNumThreads()
Definition Process.cc:1380
void publish()
Definition Process.cc:944
void transferExecProcessSignals(Thread *pThread)
Definition Process.cc:1342
bool transitionState(ProcessState expected, ProcessState desired)
Definition Process.cc:1621
bool selectPendingChildTransition(bool includeStopped, bool includeContinued, bool consume, ChildTransition &transition)
Definition Process.cc:2215
WaitQueue m_ExternalLeaseWaiters
Definition Process.h:1121
OperationBarrier m_DeferredThreadReaps
Definition Process.h:1323
Spinlock m_ExternalLeaseLock
Definition Process.h:1118
VirtualAddressSpace * m_pAddressSpace
Definition Process.h:1037
bool m_bTerminationRendezvousStarted
Definition Process.h:1177
void suspendIfContinuationEpoch(int stopSignal, size_t continuationEpoch)
Definition Process.cc:2055
OperationBarrier m_TimeAccountingReports
Definition Process.h:1320
void releaseVforkAddressSpace()
Definition Process.cc:711
void installTerminalOwner(Thread *owner)
Definition Process.cc:1592
MUST_USE_RESULT bool acquireThreadById(ThreadLease &lease, size_t id)
Definition Process.cc:1446
virtual ~Process()
Definition Process.cc:1080
Spinlock m_Lock
Definition Process.h:1204
bool m_bDestroying
Definition Process.h:1155
void suspend(int stopSignal=0)
Definition Process.cc:2051
WaitQueue m_ThreadJoinWaiters
Definition Process.h:1106
Process()
Definition Process.cc:494
MUST_USE_RESULT bool acquireThread(ThreadLease &lease, size_t n)
Definition Process.cc:1385
void resume()
Definition Process.cc:2155
void transitionToTerminating()
Definition Process.cc:1626
bool waitUntilTerminationReapable()
Definition Process.cc:2270
Time::Timestamp getUserTime() const
Definition Process.h:829
Process * addressSpaceOwner()
Definition Process.h:545
bool m_bTerminationCleanupStarted
Definition Process.h:1186
bool terminatingThreadReapable(Thread *pThread, bool &wakeOwner)
Definition Process.cc:2308
void prepareForDestruction()
Definition Process.cc:1029
TerminalOwnerReservation reserveTerminalOwner()
Definition Process.cc:1560
bool m_bTimeAccountingReportsEnabled
Definition Process.h:1326
DeferredTimeAccounting m_DeferredTimeAccounting
Definition Process.h:1317
size_t m_Id
Definition Process.h:1020
void threadExiting(Thread *pThread)
Definition Process.cc:1336
User * getUser() const
Mutex m_FilesystemContextLock
Definition Process.h:1044
void suspendInternal(int stopSignal, bool checkContinuationEpoch, size_t continuationEpoch)
Definition Process.cc:2059
size_t m_nExternalLeases
Definition Process.h:1124
ReaperClaim tryClaimReaper()
Definition Process.cc:2001
virtual int64_t getUserId() const
Definition Process.cc:2238
void kill() NORETURN
Definition Process.cc:2019
size_t getContinuationEpoch()
Definition Process.cc:2150
bool m_bTerminationSealed
Definition Process.h:1189
WaitQueue m_SuspensionWaiters
Definition Process.h:1100
bool m_bExternalLeaseReleaseInProgress
Definition Process.h:1130
SharedPointer< ControllingTerminal > m_Ctty
Definition Process.h:1050
ChildTransition m_PendingChildTransition
Definition Process.h:1133
void finishTermination(bool notifyParent=false) NORETURN
Definition Process.cc:1925
void publishReaperClaim()
Definition Process.cc:2010
MemoryAllocator m_DynamicSpaceAllocator
Definition Process.h:1058
MemoryAllocator m_SpaceAllocator
Definition Process.h:1054
void releaseThreadLease(Thread *thread)
Definition Process.cc:1296
void setTimeAccountingReportInterest(size_t interest, bool enabled)
Definition Process.cc:844
Thread * m_pTerminatingThread
Definition Process.h:1164
void publishTerminationStatus(bool notifyParent)
Definition Process.cc:2329
WaitQueue m_ChildStateWaiters
Definition Process.h:1094
bool m_bThreadJoinAdmissionClosed
Definition Process.h:1112
ProcessState m_State
Definition Process.h:1136
size_t addThread(Thread *pThread)
Definition Process.cc:1303
static void setInit(Process *pProcess)
Definition Process.cc:2421
WaitQueue m_TerminationWaiters
Definition Process.h:1097
Vector< Thread * > m_Threads
Definition Process.h:1012
void enableTimeAccountingReports(size_t initialInterest=~size_t(0))
Definition Process.cc:839
Spinlock m_TimeAccountingLock
Definition Process.h:1211
SharedPointer< UserspacePid > m_UserspacePid
Definition Process.h:1024
size_t m_ContinuationEpoch
Definition Process.h:1103
static bool getInterrupts()
static ProcessorInformation & information()
static void switchAddressSpace(VirtualAddressSpace &AddressSpace)
static void setInterrupts(bool bEnable)
void swap(RangeList &other) noexcept
Definition RangeList.h:95
This class manages how processes and threads are scheduled across processors.
Definition Scheduler.h:50
Process * getChildProcess(Process *pParent, size_t n)
Definition Scheduler.cc:485
static Scheduler & instance()
Definition Scheduler.h:96
size_t reserveProcessId()
Definition Scheduler.cc:178
MUST_USE_RESULT bool acquireProcess(ProcessLease &lease, size_t n)
Definition Scheduler.cc:275
void addProcess(Process *pProcess)
Definition Scheduler.cc:213
void yield()
Definition Scheduler.cc:236
void removeProcess(Process *pProcess)
Definition Scheduler.cc:219
static SharedPointer< T > tryAdopt(T *ptr)
static SharedPointer< T > tryAllocate(Args...)
T * get() const
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
virtual void preserveProcessSignalsForThreadExit(Thread *thread)
Definition Subsystem.h:183
virtual bool kill(KillReason killReason, Thread *pThread=0)=0
virtual void threadExiting(Thread *pThread)
Definition Subsystem.h:186
virtual void prepareThreadsForExec(Thread *owner)
Definition Subsystem.h:180
virtual void acquire()
Acquire full mutual exclusion for all Subsystem resources.
Definition Subsystem.cc:25
virtual void threadRemoved(Thread *pThread)
Definition Subsystem.h:189
virtual void threadException(Thread *pThread, ExceptionType eType, InterruptState *pState=nullptr, uintptr_t faultAddress=0, uintptr_t errorCode=0)
Definition Subsystem.cc:33
WaitQueue m_JoinWaiters
Definition Thread.h:1265
WaitQueue m_EventWaiters
Definition Thread.h:1288
void wakeForDeliverableEvents()
Definition Thread.cc:2601
@ Continue
No unwind necessary, carry on as normal.
Definition Thread.h:517
@ TerminateThread
Exit only this thread during Process exit.
Definition Thread.h:519
bool m_bReapable
Definition Thread.h:1370
volatile Status m_Status
Definition Thread.h:1310
bool m_bThreadExitRequested
Definition Thread.h:1364
bool m_bJoinClaimed
Definition Thread.h:1358
bool m_bShutdown
Definition Thread.h:1339
Time::Timestamp getUserTime() const
Definition Thread.h:412
Status getStatus() const
Definition Thread.h:433
bool m_bExitStarted
Definition Thread.h:1367
UnwindType getUnwindState()
Definition Thread.h:535
void endExternalLease()
Definition Thread.cc:2862
bool m_bProcessExitParticipant
Definition Thread.h:1376
bool m_bProcessExitOwned
Definition Thread.h:1373
Spinlock m_Lock
Definition Thread.h:1262
Process * getParent() const
Definition Thread.h:340
size_t getId()
Definition Thread.h:465
class PerProcessorScheduler * getScheduler() const
Definition Thread.h:929
bool beginExternalLease()
Definition Thread.cc:2852
void closeExternalLeaseAdmissionAndDrain()
Definition Thread.cc:2951
Definition User.h:32
A vector / dynamic array.
Definition Vector.h:33
Iterator end()
Definition Vector.h:172
T * Iterator
Definition Vector.h:36
Iterator begin()
Definition Vector.h:162
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)
Definition WaitQueue.cc:104
MUST_USE_RESULT WakeReason waitForCompletion(const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0)
Definition WaitQueue.cc:117
Special wrapper object for Process.
Definition ZombieQueue.h:38
@ Dec
Definition Log.h:126
void erase(size_t index)
Definition Vector.h:389
void pushBack(const T &value)
Definition Vector.h:275
size_t count() const
Definition Vector.h:270
ssize_t physicalPages
Physical address space consumed, barring that which is shared.
Definition Process.h:1289
ssize_t sharedPages
Shared pages consumed.
Definition Process.h:1291
ssize_t heapUsage
Bytes used in the kernel heap by this process.
Definition Process.h:1284
Time::Timestamp reapedChildrenUserTime
Time spent in userspace by children this process has reaped.
Definition Process.h:1297
Time::Timestamp userTime
CPU time retained after threads leave the process topology.
Definition Process.h:1294
Time::Timestamp startTime
Time at which process started.
Definition Process.h:1313
Time::Timestamp reapedChildrenKernelTime
Time spent in the kernel by children this process has reaped.
Definition Process.h:1299