Mercurial > ecos
diff packages/kernel/current/src/sched/sched.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/sched.cxx @@ -0,0 +1,374 @@ +//========================================================================== +// +// sched/sched.cxx +// +// Scheduler class implementations +// +//========================================================================== +//####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-15 +// Purpose: Scheduler class implementation +// Description: This file contains the definitions of the scheduler class +// member functions that are common to all scheduler +// implementations. +// +//####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/instrmnt.h> // instrumentation + +#include <cyg/kernel/sched.hxx> // our header + +#include <cyg/kernel/thread.hxx> // thread classes +#include <cyg/kernel/intr.hxx> // Interrupt interface + +#include <cyg/hal/hal_arch.h> // Architecture specific definitions + +#include <cyg/kernel/thread.inl> // thread inlines + +//------------------------------------------------------------------------- +// 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_UNLOCK_INNER +# define CYGDBG_KERNEL_TRACE_UNLOCK_INNER +# endif // control not already defined + +# endif // trace implementation not ..._SIMPLE && not ..._FANCY +#endif // CYGDBG_USE_TRACING + +// ------------------------------------------------------------------------- +// Static Cyg_Scheduler class members + +// We start with sched_lock at 1 so that any kernel code we +// call during initialization will not try to reschedule. + +volatile cyg_ucount32 Cyg_Scheduler_Base::sched_lock = 1; + +Cyg_Thread *Cyg_Scheduler_Base::current_thread = NULL; + +cyg_bool Cyg_Scheduler_Base::need_reschedule = false; + +Cyg_Scheduler Cyg_Scheduler::scheduler CYG_INIT_PRIORITY( SCHEDULER ); + +cyg_ucount32 Cyg_Scheduler_Base::thread_switches = 0; + +// ------------------------------------------------------------------------- +// Scheduler unlock function. +// This is only called when the lock is to be decremented to zero and there +// is the potential for real work to be done. Other cases are handled in +// Cyg_Scheduler::unlock() which is an inline. + +void Cyg_Scheduler::unlock_inner() +{ +#ifdef CYGDBG_KERNEL_TRACE_UNLOCK_INNER + CYG_REPORT_FUNCTION(); +#endif + + do { + + CYG_PRECONDITION( sched_lock == 1 , "sched_lock not 1" ); + +#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS + + // Call any pending DSRs. Do this here to ensure that any + // threads that get awakened are properly scheduled. + + if( Cyg_Interrupt::DSRs_pending() ) + Cyg_Interrupt::call_pending_DSRs(); +#endif + + Cyg_Thread *current = current_thread; + + CYG_ASSERTCLASS( current, "Bad current thread" ); + + // If the current thread is going to sleep, or someone + // wants a reschedule, choose another thread to run + + if( current->state != Cyg_Thread::RUNNING || need_reschedule ) { + + CYG_INSTRUMENT_SCHED(RESCHEDULE,0,0); + + // Get the next thread to run from scheduler + Cyg_Thread *next = scheduler.schedule(); + + CYG_CHECK_DATA_PTR( next, "Invalid next thread pointer"); + CYG_ASSERTCLASS( next, "Bad next thread" ); + + if( current != next ) + { + + CYG_INSTRUMENT_THREAD(SWITCH,current,next); + + // Count this thread switch + thread_switches++; + + // Switch contexts + HAL_THREAD_SWITCH_CONTEXT( ¤t->stack_ptr, + &next->stack_ptr ); + + // Worry here about possible compiler + // optimizations across the above call that may try to + // propogate common subexpresions. We would end up + // with the expression from one thread in its + // successor. This is only a worry if we do not save + // and restore the complete register set. We need a + // way of marking functions that return into a + // different context. A temporary fix would be to + // disable CSE (-fdisable-cse) in the compiler. + + // We return here only when the current thread is + // rescheduled. There is a bit of housekeeping to do + // here before we are allowed to go on our way. + + CYG_CHECK_DATA_PTR( current, "Invalid current thread pointer"); + CYG_ASSERTCLASS( current, "Bad current thread" ); + + current_thread = current; // restore current thread pointer + + } + +#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE + // Reset the timeslice counter so that this thread gets a full + // quantum. + reset_timeslice_count(); +#endif + + need_reschedule = false; // finished rescheduling + } + + sched_lock = 0; // Clear the lock + +#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS + + // Now check whether any DSRs got posted during the thread + // switch and if so, go around again. Making this test after + // the lock has been zeroed avoids a race condition in which + // a DSR could have been posted during a reschedule, but would + // not be run until the _next_ time we release the sched lock. + + HAL_REORDER_BARRIER(); + + if( Cyg_Interrupt::DSRs_pending() ) { + sched_lock = 1; // reclaim the lock + continue; // go back to head of loop + } + +#endif + // Otherwise the lock is zero, we can return. + + CYG_POSTCONDITION( sched_lock == 0, "sched_lock not zero" ); + +#ifdef CYGDBG_KERNEL_TRACE_UNLOCK_INNER + CYG_REPORT_RETURN(); +#endif + return; + + } while( 1 ); + + CYG_FAIL( "Should not be executed" ); +} + +// ------------------------------------------------------------------------- +// Start the scheduler. This is called after the initial threads have been +// created to start scheduling. + +void Cyg_Scheduler::start() +{ + CYG_REPORT_FUNCTION(); + + // Get the first thread to run from scheduler + register Cyg_Thread *next = scheduler.schedule(); + + CYG_ASSERTCLASS( next, "Bad initial thread" ); + + need_reschedule = false; // finished rescheduling + current_thread = next; // restore current thread pointer + + // Let the interrupts go + Cyg_Interrupt::enable_interrupts(); + + HAL_THREAD_LOAD_CONTEXT( &next->stack_ptr ); + +} + +// ------------------------------------------------------------------------- +// Consistency checker + +#ifdef CYGDBG_USE_ASSERTS + +bool Cyg_Scheduler::check_this( cyg_assert_class_zeal zeal) +{ + CYG_REPORT_FUNCTION(); + + // check that we have a non-NULL pointer first + if( this == NULL ) return false; + + switch( zeal ) + { + case cyg_system_test: + case cyg_extreme: + case cyg_thorough: + if( !current_thread->check_this(zeal) ) return false; + case cyg_quick: + case cyg_trivial: + case cyg_none: + default: + break; + }; + + return true; +} + +#endif + +//========================================================================== +// SchedThread members + +// ------------------------------------------------------------------------- +// Constructor + +Cyg_SchedThread::Cyg_SchedThread(Cyg_Thread *thread, CYG_ADDRWORD sched_info) +: Cyg_SchedThread_Implementation(sched_info) +{ + CYG_REPORT_FUNCTION(); + + queue = NULL; + + if( Cyg_Scheduler::current_thread == NULL ) + Cyg_Scheduler::current_thread = thread; + +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE_SIMPLE + + mutex_count = 0; + priority_inherited = false; + +#endif + +} + +// ------------------------------------------------------------------------- +// Priority inheritance support. + +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE + +// ------------------------------------------------------------------------- +// Inherit the priority of the provided thread if it +// has a higher priority than ours. + +void Cyg_SchedThread::inherit_priority( Cyg_Thread *thread) +{ +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE_SIMPLE + + // A simple implementation of priority inheritance. If the other + // thread is of higher priority, reset our priority to his. The + // first time we do this, save our original priority. + + Cyg_Thread *self = CYG_CLASSFROMBASE(Cyg_Thread, + Cyg_SchedThread, + this); + + CYG_ASSERT( mutex_count > 0, "Non-positive mutex count"); + CYG_ASSERT( self != thread, "Trying to inherit from self!"); + + if( thread->get_priority() < priority ) + { + cyg_priority mypri = priority; + + if( !priority_inherited ) + { + // If this is first inheritance, copy the old pri + // and set inherited flag. We do this after setting the + // pri since set_priority() is inheritance aware. + + self->set_priority( thread->get_priority() ); + priority_inherited = true, + original_priority = mypri; + } + else + { + // Already in inherited state, and new pri is higher + // than old. Just change the pri. + + self->set_priority( thread->get_priority() ); + } + } + +#endif +} + +// ------------------------------------------------------------------------- +// Lose a priority inheritance + +void Cyg_SchedThread::disinherit_priority() +{ +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE_SIMPLE + + // A simple implementation of priority inheritance. The + // simplification in this algorithm is that we do not reduce our + // priority until we have freed all mutexes claimed. Hence we can + // continue to run at an artificially high priority even when we + // should not. However, since nested mutexes are rare, the thread + // we have inherited from is likely to be locking the same mutexes + // we are, and mutex claim periods should be very short, the + // performance difference between this and a more complex algorithm + // should be negligible. The most important advantage of this + // algorithm is that it is fast and deterministic. + + Cyg_Thread *self = CYG_CLASSFROMBASE(Cyg_Thread, + Cyg_SchedThread, + this); + + CYG_ASSERT( mutex_count >= 0, "Non-positive mutex count"); + + if( mutex_count == 0 && priority_inherited ) + { + priority_inherited = false; + + // Only make an effort if the priority must change + if( priority < original_priority ) + self->set_priority( original_priority ); + + } + +#endif +} + +#endif + +// ------------------------------------------------------------------------- +// EOF sched/sched.cxx
