Started working on a test application for the scheduler; it uses the scheduler
of the kernel directly, and emulates the kernel's API where necessary. Not complete at all yet, but I already found one serious bug in our current scheduler with it :-) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22513 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
9d12401dcf
commit
dd980a197c
@ -57,5 +57,6 @@ SimpleTest set_area_protection_test1 : set_area_protection_test1.cpp ;
|
||||
SubInclude HAIKU_TOP src tests system kernel cache ;
|
||||
#SubInclude HAIKU_TOP src tests system kernel disk_device_manager ;
|
||||
SubInclude HAIKU_TOP src tests system kernel device_manager ;
|
||||
SubInclude HAIKU_TOP src tests system kernel scheduler ;
|
||||
SubInclude HAIKU_TOP src tests system kernel slab ;
|
||||
SubInclude HAIKU_TOP src tests system kernel util ;
|
||||
|
24
src/tests/system/kernel/scheduler/Jamfile
Normal file
24
src/tests/system/kernel/scheduler/Jamfile
Normal file
@ -0,0 +1,24 @@
|
||||
SubDir HAIKU_TOP src tests system kernel scheduler ;
|
||||
|
||||
#SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src system kernel util ] ;
|
||||
|
||||
UsePrivateHeaders kernel ;
|
||||
UsePrivateHeaders [ FDirName kernel arch $(TARGET_ARCH) ] ;
|
||||
UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ;
|
||||
#UseHeaders [ FDirName $(HAIKU_TOP) src system kernel cache ] ;
|
||||
|
||||
local includes = -include $(SUBDIR)/override_types.h ;
|
||||
local defines = ; #-DTRACE_SCHEDULER ;
|
||||
|
||||
SubDirCcFlags $(defines) $(includes) -fno-exceptions -fno-rtti ;
|
||||
SubDirC++Flags $(defines) $(includes) -fno-exceptions -fno-rtti ;
|
||||
|
||||
SimpleTest SchedulerTest :
|
||||
main.cpp
|
||||
scheduler.cpp
|
||||
: libkernelland_emu.so be
|
||||
;
|
||||
|
||||
SEARCH on [ FGristFiles
|
||||
scheduler.cpp
|
||||
] = [ FDirName $(HAIKU_TOP) src system kernel ] ;
|
390
src/tests/system/kernel/scheduler/main.cpp
Normal file
390
src/tests/system/kernel/scheduler/main.cpp
Normal file
@ -0,0 +1,390 @@
|
||||
#include "override_types.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <List.h>
|
||||
#include <Locker.h>
|
||||
#include <String.h>
|
||||
#include <TLS.h>
|
||||
|
||||
#include <cpu.h>
|
||||
#include <kscheduler.h>
|
||||
#include <timer.h>
|
||||
|
||||
|
||||
const static option kOptions[] = {
|
||||
{"time", required_argument, NULL, 't'},
|
||||
{"cpu", required_argument, NULL, 'c'},
|
||||
};
|
||||
|
||||
const bigtime_t kQuantum = 3000;
|
||||
const uint32 kMaxCPUCount = 64;
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
Thread(const char* name, int32 priority);
|
||||
virtual ~Thread();
|
||||
|
||||
struct thread* GetThread() { return &fThread; }
|
||||
virtual void Rescheduled() { fOnCPU[fThread.cpu->cpu_num]++; }
|
||||
|
||||
virtual bigtime_t NextQuantum() { return kQuantum; }
|
||||
virtual bigtime_t NextState() { return B_THREAD_READY; }
|
||||
virtual bigtime_t NextReady() { return 0; }
|
||||
|
||||
protected:
|
||||
struct thread fThread;
|
||||
int32 fOnCPU[kMaxCPUCount];
|
||||
};
|
||||
|
||||
class IdleThread : public Thread {
|
||||
public:
|
||||
IdleThread();
|
||||
virtual ~IdleThread();
|
||||
};
|
||||
|
||||
class CPU {
|
||||
public:
|
||||
CPU(int32 num);
|
||||
~CPU();
|
||||
|
||||
cpu_ent* GetCPU() { return &fCPU; }
|
||||
int32 Index() { return fCPU.cpu_num; }
|
||||
|
||||
struct thread* CurrentThread()
|
||||
{ return fCurrentThread->GetThread(); }
|
||||
void SetCurrentThread(struct thread* thread)
|
||||
{ fCurrentThread = thread->object; }
|
||||
|
||||
void Quit();
|
||||
|
||||
private:
|
||||
void _Run();
|
||||
static status_t _Run(void* self);
|
||||
|
||||
struct cpu_ent fCPU;
|
||||
thread_id fThread;
|
||||
Thread* fCurrentThread;
|
||||
IdleThread fIdleThread;
|
||||
int32 fRescheduleCount;
|
||||
bool fQuit;
|
||||
};
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
Timer();
|
||||
~Timer();
|
||||
|
||||
void AddTimer(bigtime_t time);
|
||||
};
|
||||
|
||||
thread_queue dead_q;
|
||||
|
||||
static BLocker sThreadLock;
|
||||
static BList sThreads;
|
||||
static int32 sNextThreadID = 1;
|
||||
static int32 sCPUIndexSlot;
|
||||
static CPU* sCPU[kMaxCPUCount];
|
||||
static size_t sCPUCount;
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
Thread::Thread(const char* name, int32 priority)
|
||||
{
|
||||
memset(&fThread, 0, sizeof(struct thread));
|
||||
fThread.name = strdup(name);
|
||||
fThread.id = sNextThreadID++;
|
||||
fThread.priority = fThread.next_priority = priority;
|
||||
fThread.state = fThread.next_state = B_THREAD_READY;
|
||||
fThread.object = this;
|
||||
fThread.last_time = system_time();
|
||||
|
||||
memset(&fOnCPU, 0, sizeof(fOnCPU));
|
||||
}
|
||||
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
printf(" %p %10Ld %s, prio %ld, cpu:", &fThread, fThread.kernel_time,
|
||||
fThread.name, fThread.priority);
|
||||
for (uint32 i = 0; i < kMaxCPUCount; i++) {
|
||||
if (fOnCPU[i])
|
||||
printf(" [%ld] %ld", i, fOnCPU[i]);
|
||||
}
|
||||
putchar('\n');
|
||||
free(fThread.name);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
IdleThread::IdleThread()
|
||||
: Thread("idle thread", B_IDLE_PRIORITY)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
IdleThread::~IdleThread()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
CPU::CPU(int32 num)
|
||||
:
|
||||
fRescheduleCount(0),
|
||||
fQuit(false)
|
||||
{
|
||||
memset(&fCPU, 0, sizeof(struct cpu_ent));
|
||||
fCPU.cpu_num = num;
|
||||
|
||||
fCurrentThread = &fIdleThread;
|
||||
fIdleThread.GetThread()->cpu = &fCPU;
|
||||
|
||||
fThread = spawn_thread(&_Run, (BString("cpu ") << num).String(),
|
||||
B_NORMAL_PRIORITY, this);
|
||||
resume_thread(fThread);
|
||||
}
|
||||
|
||||
|
||||
CPU::~CPU()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CPU::Quit()
|
||||
{
|
||||
fQuit = true;
|
||||
|
||||
status_t status;
|
||||
wait_for_thread(fThread, &status);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CPU::_Run()
|
||||
{
|
||||
tls_set(sCPUIndexSlot, this);
|
||||
printf("CPU %d has started.\n", fCPU.cpu_num);
|
||||
|
||||
while (!fQuit) {
|
||||
fRescheduleCount++;
|
||||
|
||||
grab_thread_lock();
|
||||
scheduler_reschedule();
|
||||
release_thread_lock();
|
||||
|
||||
fCurrentThread->Rescheduled();
|
||||
snooze(fCurrentThread->NextQuantum());
|
||||
fCurrentThread->GetThread()->next_state = fCurrentThread->NextState();
|
||||
}
|
||||
|
||||
thread_info info;
|
||||
get_thread_info(find_thread(NULL), &info);
|
||||
|
||||
printf("CPU %d has halted, user %Ld, %ld rescheduled.\n",
|
||||
fCPU.cpu_num, info.user_time, fRescheduleCount);
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CPU::_Run(void* self)
|
||||
{
|
||||
CPU* cpu = (CPU*)self;
|
||||
cpu->_Run();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Emulation
|
||||
|
||||
|
||||
extern "C" void
|
||||
kprintf(const char *format,...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
printf("\33[35m");
|
||||
vprintf(format, args);
|
||||
printf("\33[0m");
|
||||
fflush(stdout);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
thread_enqueue(struct thread *t, struct thread_queue *q)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
struct thread *
|
||||
thread_get_current_thread(void)
|
||||
{
|
||||
CPU* cpu = (CPU*)tls_get(sCPUIndexSlot);
|
||||
return cpu->CurrentThread();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
grab_thread_lock(void)
|
||||
{
|
||||
sThreadLock.Lock();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
release_thread_lock(void)
|
||||
{
|
||||
sThreadLock.Unlock();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
arch_thread_context_switch(struct thread *t_from, struct thread *t_to)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
arch_thread_set_current_thread(struct thread *thread)
|
||||
{
|
||||
CPU* cpu = (CPU*)tls_get(sCPUIndexSlot);
|
||||
//printf("[%ld] %p %s\n", cpu->Index(), thread, thread->name);
|
||||
return cpu->SetCurrentThread(thread);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
add_debugger_command(char *name, int (*func)(int, char **), char *desc)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
cpu_status
|
||||
disable_interrupts()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
restore_interrupts(cpu_status status)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
extern "C" status_t
|
||||
_local_timer_cancel_event(int cpu, timer *event)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
add_timer(timer *event, timer_hook hook, bigtime_t period, int32 flags)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
smp_get_current_cpu(void)
|
||||
{
|
||||
return thread_get_current_thread()->cpu->cpu_num;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static void
|
||||
start_cpus(uint32 count)
|
||||
{
|
||||
sCPUIndexSlot = tls_allocate();
|
||||
sCPUCount = count;
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
sCPU[i] = new CPU(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
stop_cpus()
|
||||
{
|
||||
for (size_t i = 0; i < sCPUCount; i++) {
|
||||
sCPU[i]->Quit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_thread(Thread* thread)
|
||||
{
|
||||
grab_thread_lock();
|
||||
sThreads.AddItem(thread);
|
||||
scheduler_enqueue_in_run_queue(thread->GetThread());
|
||||
release_thread_lock();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
delete_threads()
|
||||
{
|
||||
for (int32 i = 0; i < sThreads.CountItems(); i++) {
|
||||
delete (Thread*)sThreads.ItemAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
bigtime_t runTime = 1000000;
|
||||
uint32 cpuCount = 1;
|
||||
|
||||
char option;
|
||||
while ((option = getopt_long(argc, argv, "", kOptions, NULL)) != -1) {
|
||||
switch (option) {
|
||||
case 't':
|
||||
runTime *= strtol(optarg, 0, NULL);
|
||||
if (runTime <= 0) {
|
||||
fprintf(stderr, "Invalid run time.\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
cpuCount = strtol(optarg, 0, NULL);
|
||||
if (cpuCount <= 0 || cpuCount > 64) {
|
||||
fprintf(stderr, "Invalid CPU count (allowed: 1-64).\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
start_cpus(cpuCount);
|
||||
|
||||
add_thread(new Thread("test 1", 5));
|
||||
add_thread(new Thread("test 2", 10));
|
||||
add_thread(new Thread("test 3", 15));
|
||||
|
||||
snooze(runTime);
|
||||
|
||||
stop_cpus();
|
||||
delete_threads();
|
||||
return 0;
|
||||
}
|
69
src/tests/system/kernel/scheduler/override_types.h
Normal file
69
src/tests/system/kernel/scheduler/override_types.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2004-2007, Haiku Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Thread definition and structures
|
||||
*/
|
||||
#ifndef _KERNEL_THREAD_TYPES_H
|
||||
#define _KERNEL_THREAD_TYPES_H
|
||||
|
||||
#define _THREAD_H
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#if 0
|
||||
struct cpu_ent {
|
||||
bool preempted;
|
||||
};
|
||||
#endif
|
||||
extern struct thread_queue dead_q;
|
||||
|
||||
struct thread {
|
||||
struct thread *queue_next; /* i.e. run queue, release queue, etc. */
|
||||
char *name;
|
||||
thread_id id;
|
||||
int32 priority;
|
||||
int32 next_priority;
|
||||
int32 state;
|
||||
int32 next_state;
|
||||
struct cpu_ent *cpu;
|
||||
struct Thread *object;
|
||||
|
||||
bool in_kernel;
|
||||
bool is_idle;
|
||||
bool was_yielded;
|
||||
|
||||
bigtime_t user_time;
|
||||
bigtime_t kernel_time;
|
||||
bigtime_t last_time;
|
||||
};
|
||||
|
||||
enum additional_thread_state {
|
||||
THREAD_STATE_FREE_ON_RESCHED = 7, // free the thread structure upon reschedule
|
||||
// THREAD_STATE_BIRTH // thread is being created
|
||||
};
|
||||
|
||||
struct thread_queue {
|
||||
struct thread *head;
|
||||
struct thread *tail;
|
||||
};
|
||||
|
||||
|
||||
static inline bool
|
||||
thread_is_idle_thread(struct thread *thread)
|
||||
{
|
||||
return thread->is_idle;
|
||||
}
|
||||
|
||||
void thread_enqueue(struct thread *t, struct thread_queue *q);
|
||||
struct thread *thread_get_current_thread(void);
|
||||
|
||||
#define GRAB_THREAD_LOCK grab_thread_lock
|
||||
#define RELEASE_THREAD_LOCK release_thread_lock
|
||||
void grab_thread_lock(void);
|
||||
void release_thread_lock(void);
|
||||
|
||||
void arch_thread_context_switch(struct thread *t_from, struct thread *t_to);
|
||||
void arch_thread_set_current_thread(struct thread *t);
|
||||
|
||||
#endif /* _KERNEL_THREAD_TYPES_H */
|
Loading…
Reference in New Issue
Block a user