xref: /haiku/src/tests/system/kernel/scheduler/main.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
1 #include "override_types.h"
2 
3 #include <getopt.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 
8 #include <KernelExport.h>
9 #include <List.h>
10 #include <Locker.h>
11 #include <String.h>
12 #include <TLS.h>
13 
14 #include <cpu.h>
15 #include <kscheduler.h>
16 #include <timer.h>
17 
18 
19 const static option kOptions[] = {
20 	{"time", required_argument, NULL, 't'},
21 	{"cpu", required_argument, NULL, 'c'},
22 };
23 
24 const bigtime_t kQuantum = 3000;
25 const uint32 kMaxCPUCount = 64;
26 
27 class Thread {
28 public:
29 	Thread(const char* name, int32 priority);
30 	virtual ~Thread();
31 
32 	struct thread* GetThread() { return &fThread; }
33 	virtual void Rescheduled() { fOnCPU[fThread.cpu->cpu_num]++; }
34 
35 	virtual bigtime_t NextQuantum() { return kQuantum; }
36 	virtual bigtime_t NextState() { return B_THREAD_READY; }
37 	virtual bigtime_t NextReady() { return 0; }
38 
39 protected:
40 	struct thread	fThread;
41 	int32			fOnCPU[kMaxCPUCount];
42 };
43 
44 class IdleThread : public Thread {
45 public:
46 	IdleThread();
47 	virtual ~IdleThread();
48 };
49 
50 class CPU {
51 public:
52 	CPU(int32 num);
53 	~CPU();
54 
55 	cpu_ent* GetCPU() { return &fCPU; }
56 	int32 Index() { return fCPU.cpu_num; }
57 
58 	struct thread* CurrentThread()
59 		{ return fCurrentThread->GetThread(); }
60 	void SetCurrentThread(struct thread* thread)
61 		{ fCurrentThread = thread->object; }
62 
63 	void Quit();
64 
65 private:
66 	void _Run();
67 	static status_t _Run(void* self);
68 
69 	struct cpu_ent	fCPU;
70 	thread_id		fThread;
71 	Thread*			fCurrentThread;
72 	IdleThread		fIdleThread;
73 	int32			fRescheduleCount;
74 	bool			fQuit;
75 };
76 
77 class Timer {
78 public:
79 	Timer();
80 	~Timer();
81 
82 	void AddTimer(bigtime_t time);
83 };
84 
85 thread_queue dead_q;
86 
87 static BLocker sThreadLock;
88 static BList sThreads;
89 static int32 sNextThreadID = 1;
90 static int32 sCPUIndexSlot;
91 static CPU* sCPU[kMaxCPUCount];
92 static size_t sCPUCount;
93 
94 
95 //	#pragma mark -
96 
97 
98 Thread::Thread(const char* name, int32 priority)
99 {
100 	memset(&fThread, 0, sizeof(struct thread));
101 	fThread.name = strdup(name);
102 	fThread.id = sNextThreadID++;
103 	fThread.priority = fThread.next_priority = priority;
104 	fThread.state = fThread.next_state = B_THREAD_READY;
105 	fThread.object = this;
106 	fThread.last_time = system_time();
107 
108 	memset(&fOnCPU, 0, sizeof(fOnCPU));
109 }
110 
111 
112 Thread::~Thread()
113 {
114 	printf("  %p %10Ld  %s, prio %ld, cpu:", &fThread, fThread.kernel_time,
115 		fThread.name, fThread.priority);
116 	for (uint32 i = 0; i < kMaxCPUCount; i++) {
117 		if (fOnCPU[i])
118 			printf(" [%ld] %ld", i, fOnCPU[i]);
119 	}
120 	putchar('\n');
121 	free(fThread.name);
122 }
123 
124 
125 //	#pragma mark -
126 
127 
128 IdleThread::IdleThread()
129 	: Thread("idle thread", B_IDLE_PRIORITY)
130 {
131 }
132 
133 
134 IdleThread::~IdleThread()
135 {
136 }
137 
138 
139 //	#pragma mark -
140 
141 
142 CPU::CPU(int32 num)
143 	:
144 	fRescheduleCount(0),
145 	fQuit(false)
146 {
147 	memset(&fCPU, 0, sizeof(struct cpu_ent));
148 	fCPU.cpu_num = num;
149 
150 	fCurrentThread = &fIdleThread;
151 	fIdleThread.GetThread()->cpu = &fCPU;
152 
153 	fThread = spawn_thread(&_Run, (BString("cpu ") << num).String(),
154 		B_NORMAL_PRIORITY, this);
155 	resume_thread(fThread);
156 }
157 
158 
159 CPU::~CPU()
160 {
161 }
162 
163 
164 void
165 CPU::Quit()
166 {
167 	fQuit = true;
168 
169 	status_t status;
170 	wait_for_thread(fThread, &status);
171 }
172 
173 
174 void
175 CPU::_Run()
176 {
177 	tls_set(sCPUIndexSlot, this);
178 	printf("CPU %d has started.\n", fCPU.cpu_num);
179 
180 	while (!fQuit) {
181 		fRescheduleCount++;
182 
183 		grab_thread_lock();
184 		scheduler_reschedule();
185 		release_thread_lock();
186 
187 		fCurrentThread->Rescheduled();
188 		snooze(fCurrentThread->NextQuantum());
189 		fCurrentThread->GetThread()->next_state = fCurrentThread->NextState();
190 	}
191 
192 	thread_info info;
193 	get_thread_info(find_thread(NULL), &info);
194 
195 	printf("CPU %d has halted, user %lld, %ld rescheduled.\n",
196 		fCPU.cpu_num, info.user_time, fRescheduleCount);
197 	delete this;
198 }
199 
200 
201 status_t
202 CPU::_Run(void* self)
203 {
204 	CPU* cpu = (CPU*)self;
205 	cpu->_Run();
206 	return B_OK;
207 }
208 
209 
210 //	#pragma mark - Emulation
211 
212 
213 extern "C" void
214 kprintf(const char *format,...)
215 {
216 	va_list args;
217 	va_start(args, format);
218 	printf("\33[35m");
219 	vprintf(format, args);
220 	printf("\33[0m");
221 	fflush(stdout);
222 	va_end(args);
223 }
224 
225 
226 void
227 thread_enqueue(struct thread *t, struct thread_queue *q)
228 {
229 }
230 
231 
232 struct thread *
233 thread_get_current_thread(void)
234 {
235 	CPU* cpu = (CPU*)tls_get(sCPUIndexSlot);
236 	return cpu->CurrentThread();
237 }
238 
239 
240 void
241 grab_thread_lock(void)
242 {
243 	sThreadLock.Lock();
244 }
245 
246 
247 void
248 release_thread_lock(void)
249 {
250 	sThreadLock.Unlock();
251 }
252 
253 
254 void
255 arch_thread_context_switch(struct thread *t_from, struct thread *t_to)
256 {
257 }
258 
259 
260 void
261 arch_thread_set_current_thread(struct thread *thread)
262 {
263 	CPU* cpu = (CPU*)tls_get(sCPUIndexSlot);
264 	//printf("[%ld]  %p  %s\n", cpu->Index(), thread, thread->name);
265 	return cpu->SetCurrentThread(thread);
266 }
267 
268 
269 int
270 add_debugger_command(char *name, int (*func)(int, char **), char *desc)
271 {
272 	return B_OK;
273 }
274 
275 
276 cpu_status
277 disable_interrupts()
278 {
279 	return 0;
280 }
281 
282 
283 void
284 restore_interrupts(cpu_status status)
285 {
286 }
287 
288 
289 extern "C" status_t
290 _local_timer_cancel_event(int cpu, timer *event)
291 {
292 	return B_OK;
293 }
294 
295 
296 status_t
297 add_timer(timer *event, timer_hook hook, bigtime_t period, int32 flags)
298 {
299 	return B_OK;
300 }
301 
302 
303 int32
304 smp_get_current_cpu(void)
305 {
306 	return thread_get_current_thread()->cpu->cpu_num;
307 }
308 
309 
310 //	#pragma mark -
311 
312 
313 static void
314 start_cpus(uint32 count)
315 {
316 	sCPUIndexSlot = tls_allocate();
317 	sCPUCount = count;
318 
319 	for (size_t i = 0; i < count; i++) {
320 		sCPU[i] = new CPU(i);
321 	}
322 }
323 
324 
325 static void
326 stop_cpus()
327 {
328 	for (size_t i = 0; i < sCPUCount; i++) {
329 		sCPU[i]->Quit();
330 	}
331 }
332 
333 
334 static void
335 add_thread(Thread* thread)
336 {
337 	grab_thread_lock();
338 	sThreads.AddItem(thread);
339 	scheduler_enqueue_in_run_queue(thread->GetThread());
340 	release_thread_lock();
341 }
342 
343 
344 static void
345 delete_threads()
346 {
347 	for (int32 i = 0; i < sThreads.CountItems(); i++) {
348 		delete (Thread*)sThreads.ItemAt(i);
349 	}
350 }
351 
352 
353 int
354 main(int argc, char** argv)
355 {
356 	bigtime_t runTime = 1000000;
357 	uint32 cpuCount = 1;
358 
359 	char option;
360 	while ((option = getopt_long(argc, argv, "", kOptions, NULL)) != -1) {
361 		switch (option) {
362 			case 't':
363 				runTime *= strtol(optarg, 0, NULL);
364 				if (runTime <= 0) {
365 					fprintf(stderr, "Invalid run time.\n");
366 					exit(1);
367 				}
368 				break;
369 			case 'c':
370 				cpuCount = strtol(optarg, 0, NULL);
371 				if (cpuCount <= 0 || cpuCount > 64) {
372 					fprintf(stderr, "Invalid CPU count (allowed: 1-64).\n");
373 					exit(1);
374 				}
375 				break;
376 		}
377 	}
378 
379 	start_cpus(cpuCount);
380 
381 	add_thread(new Thread("test 1", 5));
382 	add_thread(new Thread("test 2", 10));
383 	add_thread(new Thread("test 3", 15));
384 
385 	snooze(runTime);
386 
387 	stop_cpus();
388 	delete_threads();
389 	return 0;
390 }
391