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:
Axel Dörfler 2007-10-12 10:23:45 +00:00
parent 9d12401dcf
commit dd980a197c
4 changed files with 484 additions and 0 deletions

View File

@ -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 ;

View 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 ] ;

View 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;
}

View 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 */