From e196b7bbc53c5c253258cf30ee58dfb10b06d352 Mon Sep 17 00:00:00 2001 From: lillo Date: Fri, 16 Aug 2002 13:14:29 +0000 Subject: [PATCH] added single-queue scheduler; the old scheduler is compiled by default - to enable the new one uncomment the #define at the beginning of thread.h git-svn-id: file:///srv/svn/repos/haiku/trunk/current@776 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/thread.h | 14 +++ src/kernel/apps/tests/thread_test.c | 20 ++++ src/kernel/core/scheduler.c | 138 ++++++++++++++++++++++++++++ src/kernel/core/thread.c | 21 +++++ 4 files changed, 193 insertions(+) diff --git a/headers/private/kernel/thread.h b/headers/private/kernel/thread.h index 3362d1004c..00cfe2615e 100755 --- a/headers/private/kernel/thread.h +++ b/headers/private/kernel/thread.h @@ -12,6 +12,9 @@ extern "C" { #include #include +// Uncomment the line below to compile the single-queue scheduler +//#define NEW_SCHEDULER + void resched(void); void start_scheduler(void); @@ -19,15 +22,23 @@ void thread_enqueue(struct thread *t, struct thread_queue *q); struct thread *thread_lookat_queue(struct thread_queue *q); struct thread *thread_dequeue(struct thread_queue *q); struct thread *thread_dequeue_id(struct thread_queue *q, thread_id thr_id); +#ifndef NEW_SCHEDULER struct thread *thread_lookat_run_q(int priority); +#endif /* not NEW_SCHEDULER */ void thread_enqueue_run_q(struct thread *t); +#ifndef NEW_SCHEDULER struct thread *thread_dequeue_run_q(int priority); +#endif /* not NEW_SCHEDULER */ void thread_atkernel_entry(void); // called when the thread enters the kernel on behalf of the thread void thread_atkernel_exit(void); int thread_suspend_thread(thread_id id); int thread_resume_thread(thread_id id); +#ifndef NEW_SCHEDULER int thread_set_priority(thread_id id, int priority); +#else /* NEW_SCHEDULER */ +status_t thread_set_priority(thread_id id, int32 priority); +#endif /* NEW_SCHEDULER */ int thread_init(kernel_args *ka); int thread_init_percpu(int cpu_num); void thread_exit(int retcode); @@ -37,6 +48,9 @@ int thread_kill_thread_nowait(thread_id id); #define thread_get_current_thread arch_thread_get_current_thread struct thread *thread_get_thread_struct(thread_id id); +#ifdef NEW_SCHEDULER +struct thread *thread_get_thread_struct_locked(thread_id id); +#endif /* NEW_SCHEDULER */ thread_id thread_get_current_thread_id(void); extern inline thread_id thread_get_current_thread_id(void) { diff --git a/src/kernel/apps/tests/thread_test.c b/src/kernel/apps/tests/thread_test.c index c230c36ef4..467e3d4a62 100644 --- a/src/kernel/apps/tests/thread_test.c +++ b/src/kernel/apps/tests/thread_test.c @@ -31,6 +31,19 @@ static int counter_thread(void *data) return 0; } + +static int priority_test(void *data) +{ + int i; + + for (i=0; i<10; i++) { + printf("%s: %d\n", (char *)data, i); +// sys_snooze(1000); + } + return 0; +} + + int main(int argc, char **argv) { thread_id t[THREADS]; @@ -62,5 +75,12 @@ int main(int argc, char **argv) delete_sem(lock); + t[0] = spawn_thread(priority_test, "thread0", B_DISPLAY_PRIORITY, "thread0"); + t[1] = spawn_thread(priority_test, "thread1", B_URGENT_DISPLAY_PRIORITY, "thread1"); + resume_thread(t[0]); + resume_thread(t[1]); + sys_wait_on_thread(t[0], NULL); + sys_wait_on_thread(t[1], NULL); + return 0; } diff --git a/src/kernel/core/scheduler.c b/src/kernel/core/scheduler.c index 8bae96f423..55f270cf69 100644 --- a/src/kernel/core/scheduler.c +++ b/src/kernel/core/scheduler.c @@ -18,9 +18,102 @@ #include #include #include +#ifdef NEW_SCHEDULER +#include +#include +#endif /* NEW_SCHEDULER */ static int _rand(void); +#ifdef NEW_SCHEDULER + +// The run queue. Holds the threads ready to run ordered by priority. +static struct thread_queue run_q = { NULL, NULL }; + + +void +thread_enqueue_run_q(struct thread *t) +{ + struct thread *curr, *prev; + + // these shouldn't exist + if(t->priority > B_MAX_PRIORITY) + t->priority = B_MAX_PRIORITY; + if(t->priority < B_MIN_PRIORITY) + t->priority = B_MIN_PRIORITY; + + if (run_q.head == NULL) { + t->q_next = NULL; + run_q.head = t; + } else { + for (curr = run_q.head, prev = NULL; curr && (curr->priority >= t->priority); curr = curr->q_next) { + if (prev) + prev = prev->q_next; + else + prev = run_q.head; + } + t->q_next = curr; + if (prev) + prev->q_next = t; + else + run_q.head = t; + } +} + + +status_t +thread_set_priority(thread_id id, int32 priority) +{ + struct thread *t, *curr, *prev; + int retval; + + // make sure the passed in priority is within bounds + if (priority > B_MAX_PRIORITY) + priority = B_MAX_PRIORITY; + if (priority < B_MIN_PRIORITY) + priority = B_MIN_PRIORITY; + + t = thread_get_current_thread(); + if (t->id == id) { + // it's ourself, so we know we aren't in the run queue, and we can manipulate + // our structure directly + retval = t->priority; + t->priority = priority; + } else { + int state = disable_interrupts(); + GRAB_THREAD_LOCK(); + + t = thread_get_thread_struct_locked(id); + if (t) { + retval = t->priority; + if ((t->state == B_THREAD_READY) && (t->priority != priority)) { + // this thread is in the ready queue right now, so it needs to be reinserted + for (curr = run_q.head, prev = NULL; curr && (curr->id != t->id); curr = curr->q_next) { + if (prev) + prev = prev->q_next; + else + prev = run_q.head; + } + if (prev) + prev->q_next = curr->q_next; + else + run_q.head = curr->q_next; + t->priority = priority; + thread_enqueue_run_q(t); + } else + t->priority = priority; + } else + retval = B_BAD_THREAD_ID; + + RELEASE_THREAD_LOCK(); + restore_interrupts(state); + } + + return retval; +} + + +#endif /* NEW_SCHEDULER */ static int _rand(void) { @@ -93,14 +186,24 @@ reschedule_event(timer *unused) void resched(void) { +#ifndef NEW_SCHEDULER struct thread *next_thread = NULL; int last_thread_pri = -1; +#else /* NEW_SCHEDULER */ + struct thread *next_thread, *prev_thread = NULL; +#endif /* NEW_SCHEDULER */ struct thread *old_thread = thread_get_current_thread(); +#ifndef NEW_SCHEDULER int i; +#endif /* not NEW_SCHEDULER */ bigtime_t quantum; timer *quantum_timer; +#ifndef NEW_SCHEDULER // dprintf("top of thread_resched: cpu %d, cur_thread = 0x%x\n", smp_get_current_cpu(), thread_get_current_thread()); +#else /* NEW_SCHEDULER */ +// dprintf("resched(): cpu %d, cur_thread = 0x%x\n", smp_get_current_cpu(), thread_get_current_thread()); +#endif /* NEW_SCHEDULER */ switch(old_thread->next_state) { case B_THREAD_RUNNING: @@ -109,7 +212,11 @@ resched(void) thread_enqueue_run_q(old_thread); break; case B_THREAD_SUSPENDED: +#ifndef NEW_SCHEDULER dprintf("suspending thread 0x%x\n", old_thread->id); +#else /* NEW_SCHEDULER */ + dprintf("resched(): suspending thread 0x%x\n", old_thread->id); +#endif /* NEW_SCHEDULER */ break; case THREAD_STATE_FREE_ON_RESCHED: // This will hopefully be eliminated once the slab @@ -122,6 +229,7 @@ resched(void) } old_thread->state = old_thread->next_state; +#ifndef NEW_SCHEDULER // search the real-time queue for(i = B_MAX_PRIORITY; i >= B_FIRST_REAL_TIME_PRIORITY; i-=2) { next_thread = thread_dequeue_run_q(i); @@ -152,9 +260,37 @@ resched(void) if(next_thread == NULL) panic("next_thread == NULL! no idle priorities!\n"); } +#else /* NEW_SCHEDULER */ + // select next thread from the run queue + next_thread = run_q.head; + while ((next_thread) && (next_thread->priority > B_IDLE_PRIORITY)) { + // always extract real time threads + if (next_thread->priority >= B_FIRST_REAL_TIME_PRIORITY) + break; + // never skip last non-idle normal thread + if (next_thread->q_next && (next_thread->q_next->priority == B_IDLE_PRIORITY)) + break; + // skip normal threads sometimes + if (_rand() > 0x3000) + break; + prev_thread = next_thread; + next_thread = next_thread->q_next; +#endif /* NEW_SCHEDULER */ } +#ifndef NEW_SCHEDULER found_thread: +#else /* NEW_SCHEDULER */ + if (!next_thread) + panic("resched(): run queue is empty!\n"); + + // extract selected thread from the run queue + if (prev_thread) + prev_thread->q_next = next_thread->q_next; + else + run_q.head = next_thread->q_next; + +#endif /* NEW_SCHEDULER */ next_thread->state = B_THREAD_RUNNING; next_thread->next_state = B_THREAD_READY; @@ -169,6 +305,7 @@ found_thread: if (next_thread != old_thread) context_switch(old_thread, next_thread); } +#ifndef NEW_SCHEDULER #if 0 // XXX should only reset the quantum timer if we are switching to a new thread, @@ -191,5 +328,6 @@ found_thread: context_switch(old_thread, next_thread); } #endif +#endif /* not NEW_SCHEDULER */ } diff --git a/src/kernel/core/thread.c b/src/kernel/core/thread.c index cf5ec3168e..ea7feb003d 100644 --- a/src/kernel/core/thread.c +++ b/src/kernel/core/thread.c @@ -61,17 +61,28 @@ static unsigned int num_death_stacks; static unsigned int volatile death_stack_bitmap; static sem_id death_stack_sem; +#ifndef NEW_SCHEDULER // thread queues // Thread priority has a granularity of 2; this means that we have 61 real // priority levels: 60 to map BeOS priorities 1-120, plus the idle priority (0). static struct thread_queue run_q[(B_MAX_PRIORITY / 2) + 1] = { { NULL, NULL }, }; +#else /* NEW_SCHEDULER */ +// The dead queue is used as a pool from which to retrieve and reuse previously +// allocated thread structs when creating a new thread. It should be gone once +// the slab allocator is in. +#endif /* NEW_SCHEDULER */ struct thread_queue dead_q; static void thread_entry(void); +#ifndef NEW_SCHEDULER static struct thread *thread_get_thread_struct_locked(thread_id id); +#endif /* not NEW_SCHEDULER */ static void thread_kthread_exit(void); static void deliver_signal(struct thread *t, int signal); +#ifdef NEW_SCHEDULER + +#endif /* NEW_SCHEDULER */ // insert a thread onto the tail of a queue void thread_enqueue(struct thread *t, struct thread_queue *q) { @@ -126,6 +137,7 @@ struct thread *thread_dequeue_id(struct thread_queue *q, thread_id thr_id) return t; } +#ifndef NEW_SCHEDULER struct thread *thread_lookat_run_q(int priority) { return thread_lookat_queue(&run_q[(priority + 1) >> 1]); @@ -147,6 +159,7 @@ struct thread *thread_dequeue_run_q(int priority) return thread_dequeue(&run_q[(priority + 1) >> 1]); } +#endif /* not NEW_SCHEDULER */ static void insert_thread_into_team(struct team *p, struct thread *t) { t->team_next = p->thread_list; @@ -483,6 +496,7 @@ int thread_resume_thread(thread_id id) return retval; } +#ifndef NEW_SCHEDULER int thread_set_priority(thread_id id, int priority) { struct thread *t; @@ -525,6 +539,7 @@ int thread_set_priority(thread_id id, int priority) return retval; } +#endif /* not NEW_SCHEDULER */ static const char *state_to_text(int state) { @@ -768,9 +783,11 @@ int thread_init(kernel_args *ka) thread_hash = hash_init(15, (addr)&t->all_next - (addr)t, &thread_struct_compare, &thread_struct_hash); +#ifndef NEW_SCHEDULER // zero out the run queues memset(run_q, 0, sizeof(run_q)); +#endif /* not NEW_SCHEDULER */ // zero out the dead thread structure q memset(&dead_q, 0, sizeof(dead_q)); @@ -1173,7 +1190,11 @@ struct thread *thread_get_thread_struct(thread_id id) return t; } +#ifndef NEW_SCHEDULER static struct thread *thread_get_thread_struct_locked(thread_id id) +#else /* NEW_SCHEDULER */ +struct thread *thread_get_thread_struct_locked(thread_id id) +#endif /* NEW_SCHEDULER */ { struct thread_key key;