diff --git a/src/tests/system/kernel/Jamfile b/src/tests/system/kernel/Jamfile index 429991288c..3e12e01131 100644 --- a/src/tests/system/kernel/Jamfile +++ b/src/tests/system/kernel/Jamfile @@ -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 ; diff --git a/src/tests/system/kernel/scheduler/Jamfile b/src/tests/system/kernel/scheduler/Jamfile new file mode 100644 index 0000000000..c7c4dcf295 --- /dev/null +++ b/src/tests/system/kernel/scheduler/Jamfile @@ -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 ] ; diff --git a/src/tests/system/kernel/scheduler/main.cpp b/src/tests/system/kernel/scheduler/main.cpp new file mode 100644 index 0000000000..f29c03c002 --- /dev/null +++ b/src/tests/system/kernel/scheduler/main.cpp @@ -0,0 +1,390 @@ +#include "override_types.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + + +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; +} diff --git a/src/tests/system/kernel/scheduler/override_types.h b/src/tests/system/kernel/scheduler/override_types.h new file mode 100644 index 0000000000..c0dc1f9bb5 --- /dev/null +++ b/src/tests/system/kernel/scheduler/override_types.h @@ -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 + +#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 */