The Pedigree Project 0.1
Scheduler-system-info.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/PerProcessorScheduler.h"
4#include "pedigree/kernel/process/Scheduler.h"
5#include "pedigree/kernel/process/Thread.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/time/Time.h"
8#include "pedigree/kernel/utilities/Pointers.h"
9
10#if THREADS
12 public:
13 struct Entry {
14 Process* process = nullptr;
15 Thread* thread = nullptr;
16 };
18 for (size_t i = 0; i < count; ++i)
19 Scheduler::releaseActivityEntry(entries.get()[i].process, entries.get()[i].thread);
20 }
21 TerminationDeferral lifetime;
22 UniqueArray<Entry> entries;
23 size_t count = 0;
24};
25
26void Scheduler::releaseActivityEntry(Process* process, Thread* thread) {
27 if (thread)
28 thread->endExternalLease();
29 if (process)
30 process->endExternalLease();
31}
32
33Scheduler::SystemActivity Scheduler::systemActivity() {
34 SystemActivity result;
35 {
37 for (auto it = m_TPMap.begin(); it != m_TPMap.end(); ++it) {
38 if (it.key() != __atomic_load_n(&it.value()->m_pIdleThread, __ATOMIC_ACQUIRE))
39 ++result.tasks;
40 }
41 }
42 {
43 LockGuard<Mutex> averages(m_ActivityLock);
44 m_LoadAverage.snapshot(result.loads);
45 }
46 result.userNanoseconds = m_UserNanoseconds;
47 result.kernelNanoseconds = m_KernelNanoseconds;
48 result.idleNanoseconds = m_IdleNanoseconds;
49 return result;
50}
51
52void Scheduler::recordCpuTime(const Thread& thread, CpuTimeMode mode, Time::Timestamp elapsed) {
53 PerProcessorScheduler* owner = thread.getScheduler();
54 if (owner && &thread == __atomic_load_n(&owner->m_pIdleThread, __ATOMIC_ACQUIRE)) {
55 m_IdleNanoseconds += elapsed;
56 } else if (mode == CpuTimeMode::User) {
57 m_UserNanoseconds += elapsed;
58 } else {
59 m_KernelNanoseconds += elapsed;
60 }
61}
62
64 if (Time::getTicks() < __atomic_load_n(&m_NextActivityAttempt, __ATOMIC_ACQUIRE))
65 return;
66 bool pending = false;
67 if (__atomic_compare_exchange_n(&m_ActivitySamplePending, &pending, true, false, __ATOMIC_ACQ_REL,
68 __ATOMIC_ACQUIRE))
69 Processor::information().getScheduler().publishDeferredTimeAccounting();
70}
71
73 // Claim before checking the deadline: a tick that saw an older deadline
74 // can publish after another worker has already advanced it.
75 __atomic_store_n(&m_ActivitySamplePending, false, __ATOMIC_RELEASE);
76 const uint64_t now = Time::getTicks();
77 if (now < __atomic_load_n(&m_NextActivityAttempt, __ATOMIC_ACQUIRE))
78 return;
79 LockGuard<Mutex> averages(m_ActivityLock);
80 if (now < m_NextActivityAttempt)
81 return;
82 const uint64_t delay = LoadAverage::PeriodNanoseconds;
83 __atomic_store_n(&m_NextActivityAttempt, now > ~uint64_t(0) - delay ? ~uint64_t(0) : now + delay,
84 __ATOMIC_RELEASE);
85
86 ActivitySample sample;
87 bool prepared = false;
88 for (unsigned attempt = 0; attempt < 4; ++attempt) {
89 size_t required;
90 {
92 required = m_TPMap.count();
93 }
94 sample.entries = UniqueArray<ActivitySample::Entry>::allocate(required);
95 if (required && !sample.entries)
96 return;
97 {
99 if (m_TPMap.count() > required)
100 continue;
101 for (auto it = m_TPMap.begin(); it != m_TPMap.end(); ++it) {
102 Thread* thread = it.key();
103 if (thread == __atomic_load_n(&it.value()->m_pIdleThread, __ATOMIC_ACQUIRE))
104 continue;
105 Process* process = thread->getParent();
106 if (!process->beginExternalLease())
107 continue;
108 auto& entry = sample.entries.get()[sample.count++];
109 entry.process = process;
110 if (thread->beginExternalLease())
111 entry.thread = thread;
112 }
113 prepared = true;
114 }
115 break;
116 }
117 if (!prepared)
118 return;
119 uint32_t active = 0;
120 Thread* observer = Processor::information().getCurrentThread();
121 for (size_t i = 0; i < sample.count; ++i) {
122 Thread* thread = sample.entries.get()[i].thread;
123 if (!thread || thread == observer)
124 continue;
125 LockGuard<Spinlock> state(thread->m_Lock);
126 const auto status = thread->getStatus();
127 // The ready queue contains only threads which can run. Exclude this
128 // observer, whose own execution protects the sampling work.
129 if ((status == Thread::Ready || status == Thread::Running) &&
130 !__atomic_load_n(&thread->m_ReadyPublicationPending, __ATOMIC_ACQUIRE))
131 if (active != ~uint32_t(0))
132 ++active;
133 }
134 // Sleeping tasks have no distinct uninterruptible-I/O classification yet.
135 m_LoadAverage.update(now, active);
136}
137
138#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
139bool Scheduler::runHostedLoadAverageRequestRegression() {
141 const uint64_t deadline = __atomic_load_n(&m_NextActivityAttempt, __ATOMIC_ACQUIRE);
142 if (Time::getTicks() >= deadline)
143 return false;
144 // Replay a tick whose due check preceded the sample, but whose request
145 // publication arrived after it. The early return must retire that request.
146 __atomic_store_n(&m_ActivitySamplePending, true, __ATOMIC_RELEASE);
148 return !__atomic_load_n(&m_ActivitySamplePending, __ATOMIC_ACQUIRE) &&
149 __atomic_load_n(&m_NextActivityAttempt, __ATOMIC_ACQUIRE) == deadline;
150}
151#endif
152#endif
void endExternalLease()
Definition Process.cc:1087
bool beginExternalLease()
Definition Process.cc:1077
static ProcessorInformation & information()
void recordCpuTime(const Thread &thread, CpuTimeMode mode, Time::Timestamp elapsed)
Spinlock m_SchedulerLock
Definition Scheduler.h:260
void requestLoadAverageSample()
Tree< Thread *, PerProcessorScheduler * > m_TPMap
Definition Scheduler.h:245
Status getStatus() const
Definition Thread.h:431
void endExternalLease()
Definition Thread.cc:2883
Spinlock m_Lock
Definition Thread.h:1257
Process * getParent() const
Definition Thread.h:338
class PerProcessorScheduler * getScheduler() const
Definition Thread.h:925
bool beginExternalLease()
Definition Thread.cc:2873
Iterator begin()
Definition Tree.h:402
Iterator end()
Definition Tree.h:427
size_t count() const
Definition Tree.h:142