Mercurial > nand-ecoscentric
diff packages/kernel/current/src/sched/mlqueue.cxx @ 0:3111d98ba7b3 ecos-v1_1-release
Initial commit of eCos version 1.1
| author | jlarmour |
|---|---|
| date | Tue, 11 May 1999 11:16:07 +0000 |
| parents | |
| children | 443894e2e912 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/kernel/current/src/sched/mlqueue.cxx @@ -0,0 +1,486 @@ +//========================================================================== +// +// sched/mlqueue.cxx +// +// Multi-level queue scheduler class implementation +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Cygnus eCos Public License +// Version 1.0 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://sourceware.cygnus.com/ecos +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Cygnus Operating System, released +// September 30, 1998. +// +// The Initial Developer of the Original Code is Cygnus. Portions created +// by Cygnus are Copyright (C) 1998 Cygnus Solutions. All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 1997-09-16 +// Purpose: Multilevel queue scheduler class implementation +// Description: This file contains the implementations of +// Cyg_Scheduler_Implementation and Cyg_SchedThread_Implementation. +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/kernel.h> + +#include <cyg/kernel/ktypes.h> // base kernel types +#include <cyg/infra/cyg_trac.h> // tracing macros +#include <cyg/infra/cyg_ass.h> // assertion macros + +#include <cyg/kernel/sched.hxx> // our header + +#include <cyg/hal/hal_arch.h> // Architecture specific definitions + +#include <cyg/kernel/thread.inl> // thread inlines +#include <cyg/kernel/sched.inl> // scheduler inlines + +#ifdef CYGSEM_KERNEL_SCHED_MLQUEUE + +//------------------------------------------------------------------------- +// Some local tracing control - a default. +#ifdef CYGDBG_USE_TRACING +# if !defined( CYGDBG_INFRA_DEBUG_TRACE_ASSERT_SIMPLE ) && \ + !defined( CYGDBG_INFRA_DEBUG_TRACE_ASSERT_FANCY ) + // ie. not a tracing implementation that takes a long time to output + +# ifndef CYGDBG_KERNEL_TRACE_TIMESLICE +# define CYGDBG_KERNEL_TRACE_TIMESLICE +# endif // control not already defined + +# endif // trace implementation not ..._SIMPLE && not ..._FANCY +#endif // CYGDBG_USE_TRACING + +//========================================================================== +// Cyg_Scheduler_Implementation class static members + +#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE + +cyg_ucount32 Cyg_Scheduler_Implementation::timeslice_count = + CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS; + +#endif + + +//========================================================================== +// Cyg_Scheduler_Implementation class members + +// ------------------------------------------------------------------------- +// Constructor. + +Cyg_Scheduler_Implementation::Cyg_Scheduler_Implementation() +{ + CYG_REPORT_FUNCTION(); + + queue_map = 0; +} + +// ------------------------------------------------------------------------- +// Choose the best thread to run next + +Cyg_Thread *Cyg_Scheduler_Implementation::schedule() +{ + CYG_REPORT_FUNCTION(); + + // The run queue may _never_ be empty, there is always + // an idle thread at the lowest priority. + + CYG_ASSERT( queue_map != 0, "Run queue empty"); + CYG_ASSERT( queue_map & (1<<CYG_THREAD_MIN_PRIORITY), "Idle thread vanished!!!"); + CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); + + register cyg_uint32 index; + + HAL_LSBIT_INDEX(index, queue_map); + + Cyg_Thread *thread = run_queue[index].highpri(); + + CYG_ASSERT( thread != NULL , "No threads in run queue"); + + return thread; +} + +// ------------------------------------------------------------------------- + +void Cyg_Scheduler_Implementation::add_thread(Cyg_Thread *thread) +{ + CYG_REPORT_FUNCTION(); + + cyg_priority pri = thread->priority; + Cyg_ThreadQueue_Implementation *queue = &run_queue[pri]; + + // If the thread is on some other queue, remove it + // here. + if( thread->queue != NULL ) + { + thread->queue->remove(thread); + thread->queue = NULL; + } + + if( queue->empty() ) + { + // set the map bit and ask for a reschedule if this is a + // new highest priority thread. + + queue_map |= (1<<pri); + + // If the new thread is higher priority than the + // current thread, request a reschedule. + + if( pri < Cyg_Scheduler::get_current_thread()->priority ) + need_reschedule = true; + + } + // else the queue already has an occupant, queue behind him + + CYG_ASSERT( queue_map != 0, "Run queue empty"); + CYG_ASSERT( queue_map & (1<<pri), "Queue map bit not set for pri"); + CYG_ASSERT( queue_map & (1<<CYG_THREAD_MIN_PRIORITY), "Idle thread vanished!!!"); +// CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); + + queue->enqueue(thread); +} + +// ------------------------------------------------------------------------- + +void Cyg_Scheduler_Implementation::rem_thread(Cyg_Thread *thread) +{ + CYG_REPORT_FUNCTION(); + + CYG_ASSERT( queue_map != 0, "Run queue empty"); + + cyg_priority pri = thread->priority; + Cyg_ThreadQueue_Implementation *queue = &run_queue[pri]; + + CYG_ASSERT( pri != CYG_THREAD_MIN_PRIORITY, "Idle thread trying to sleep!"); + CYG_ASSERT( queue_map & (1<<pri), "Queue map bit not set for pri"); + CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); + + // remove thread from queue + queue->remove(thread); + + if( queue->empty() ) + { + // If this was only thread in + // queue, clear map. + + queue_map &= ~(1<<pri); + } + + CYG_ASSERT( queue_map != 0, "Run queue empty"); + CYG_ASSERT( queue_map & (1<<CYG_THREAD_MIN_PRIORITY), "Idle thread vanished!!!"); + CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); +} + +// ------------------------------------------------------------------------- +// register thread with scheduler + +void Cyg_Scheduler_Implementation::register_thread(Cyg_Thread *thread) +{ + CYG_REPORT_FUNCTION(); + + // No registration necessary in this scheduler +} + +// ------------------------------------------------------------------------- + +// deregister thread +void Cyg_Scheduler_Implementation::deregister_thread(Cyg_Thread *thread) +{ + CYG_REPORT_FUNCTION(); + + // No registration necessary in this scheduler +} + +// ------------------------------------------------------------------------- +// Test the given priority for uniqueness + +cyg_bool Cyg_Scheduler_Implementation::unique( cyg_priority priority) +{ + CYG_REPORT_FUNCTION(); + + // Priorities are not unique + return true; +} + +//========================================================================== +// Support for timeslicing option + +#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE + +void Cyg_Scheduler_Implementation::timeslice() +{ +#ifdef CYGDBG_KERNEL_TRACE_TIMESLICE + CYG_REPORT_FUNCTION(); +#endif + CYG_ASSERT( queue_map != 0, "Run queue empty"); + CYG_ASSERT( queue_map & (1<<CYG_THREAD_MIN_PRIORITY), "Idle thread vanished!!!"); + + if( --timeslice_count == 0 ) + { + CYG_INSTRUMENT_SCHED(TIMESLICE,0,0); +#ifdef CYGDBG_KERNEL_TRACE_TIMESLICE + CYG_TRACE0( true, "quantum consumed, time to reschedule" ); +#endif + // And force the current thread to yield. + current_thread->yield(); + } + + CYG_ASSERT( queue_map & (1<<CYG_THREAD_MIN_PRIORITY), "Idle thread vanished!!!"); + CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); +#ifdef CYGDBG_KERNEL_TRACE_TIMESLICE + CYG_REPORT_RETURN(); +#endif +} + +#endif + +//========================================================================== +// Cyg_Cyg_SchedThread_Implementation class members + +Cyg_SchedThread_Implementation::Cyg_SchedThread_Implementation +( + CYG_ADDRWORD sched_info +) +{ + CYG_REPORT_FUNCTION(); + + // Create all threads at maximum priority + priority = (cyg_priority)sched_info; + + // point the next and prev field at this thread. + + next = prev = CYG_CLASSFROMBASE(Cyg_Thread, + Cyg_SchedThread_Implementation, + this); +} + +// ------------------------------------------------------------------------- +// Insert thread in front of this + +void Cyg_SchedThread_Implementation::insert( Cyg_Thread *thread) +{ + CYG_REPORT_FUNCTION(); + + thread->next = CYG_CLASSFROMBASE(Cyg_Thread, + Cyg_SchedThread_Implementation, + this); + thread->prev = prev; + prev->next = thread; + prev = thread; +} + +// ------------------------------------------------------------------------- +// remove this from queue + +void Cyg_SchedThread_Implementation::remove() +{ + CYG_REPORT_FUNCTION(); + + next->prev = prev; + prev->next = next; + next = prev = CYG_CLASSFROMBASE(Cyg_Thread, + Cyg_SchedThread_Implementation, + this); +} + +// ------------------------------------------------------------------------- +// Yield the processor to another thread + +void Cyg_SchedThread_Implementation::yield() +{ + CYG_REPORT_FUNCTION(); + + // Prevent preemption + Cyg_Scheduler::lock(); + + Cyg_Thread *thread = CYG_CLASSFROMBASE(Cyg_Thread, + Cyg_SchedThread_Implementation, + this); + + // Only do this if this thread is running. If it is not, there + // is no point. + + if( thread->get_state() == Cyg_Thread::RUNNING ) + { + // To yield we simply rotate the appropriate + // run queue to the next thread and reschedule. + + CYG_ASSERTCLASS( thread, "Bad current thread"); + + Cyg_Scheduler *sched = &Cyg_Scheduler::scheduler; + + CYG_ASSERTCLASS( sched, "Bad scheduler"); + + cyg_priority pri = thread->priority; + Cyg_ThreadQueue_Implementation *queue = &sched->run_queue[pri]; + + queue->rotate(); + + if( queue->highpri() != thread ) + sched->need_reschedule = true; +#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE + // Reset the timeslice counter so that this thread gets a full + // quantum. + else Cyg_Scheduler::reset_timeslice_count(); +#endif + } + + // Unlock the scheduler and switch threads + Cyg_Scheduler::unlock(); + +} + +// ------------------------------------------------------------------------- +// Rotate the run queue at a specified priority. +// (pri is the decider, no this, so the routine is static) + +void +Cyg_SchedThread_Implementation::rotate_queue( cyg_priority pri ) +{ + CYG_REPORT_FUNCTION(); + + // Prevent preemption + Cyg_Scheduler::lock(); + + Cyg_Scheduler *sched = &Cyg_Scheduler::scheduler; + + CYG_ASSERTCLASS( sched, "Bad scheduler"); + + Cyg_ThreadQueue_Implementation *queue = &sched->run_queue[pri]; + + if ( !queue->empty() ) { + queue->rotate(); + sched->need_reschedule = true; + } + + // Unlock the scheduler and switch threads + Cyg_Scheduler::unlock(); + +} + +//========================================================================== +// Cyg_ThreadQueue_Implementation class members + +Cyg_ThreadQueue_Implementation::Cyg_ThreadQueue_Implementation() +{ + CYG_REPORT_FUNCTION(); + + queue = NULL; // empty queue +} + + + +void Cyg_ThreadQueue_Implementation::enqueue(Cyg_Thread *thread) +{ + CYG_REPORT_FUNCTION(); + + if( queue == NULL ) queue = thread; + else queue->insert(thread); + + thread->queue = CYG_CLASSFROMBASE(Cyg_ThreadQueue, + Cyg_ThreadQueue_Implementation, + this); + +} + +// ------------------------------------------------------------------------- + +Cyg_Thread *Cyg_ThreadQueue_Implementation::dequeue() +{ + CYG_REPORT_FUNCTION(); + + if( queue == NULL ) return NULL; + + Cyg_Thread *thread = queue; + + if( thread->next == thread ) + { + // sole thread on list, NULL out ptr + queue = NULL; + } + else + { + // advance to next and remove thread + queue = thread->next; + thread->remove(); + } + + thread->queue = NULL; + + return thread; +} + +// ------------------------------------------------------------------------- + +Cyg_Thread *Cyg_ThreadQueue_Implementation::highpri() +{ + CYG_REPORT_FUNCTION(); + + return queue; +} + +// ------------------------------------------------------------------------- + +void Cyg_ThreadQueue_Implementation::remove(Cyg_Thread *thread) +{ + CYG_REPORT_FUNCTION(); + + // If the thread we want it the at the head + // of the list, and is on its own, clear the + // list and return. Otherwise advance to the + // next thread and remove ours. If the thread + // is not at the head of the list, just dequeue + // it. + + thread->queue = NULL; + + if( queue == thread ) + { + if( thread->next == thread ) + { + queue = NULL; + return; + } + else queue = thread->next; + } + + thread->Cyg_SchedThread_Implementation::remove(); + +} + +// ------------------------------------------------------------------------- +// Rotate the front thread on the queue to the back. + +void Cyg_ThreadQueue_Implementation::rotate() +{ + CYG_REPORT_FUNCTION(); + + queue = queue->next; +} + +// ------------------------------------------------------------------------- + +#endif + +// ------------------------------------------------------------------------- +// EOF sched/mlqueue.cxx
