Mercurial > ecos-v3_0-branch
diff packages/kernel/current/src/common/thread.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/common/thread.cxx @@ -0,0 +1,1234 @@ +//========================================================================== +// +// common/thread.cxx +// +// Thread 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: Thread class implementation +// Description: This file contains the definitions of the thread class +// member functions that are common to all thread implementations. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/kernel.h> // kernel configuration file + +#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/thread.hxx> // our header + +#include <cyg/kernel/intr.hxx> // Interrupt support + +#include <cyg/kernel/thread.inl> // thread inlines +#include <cyg/kernel/sched.inl> // scheduler inlines +#include <cyg/kernel/clock.inl> // clock inlines + +// ========================================================================= +// Cyg_HardwareThread members + +// ------------------------------------------------------------------------- +// Thread entry point. +// This is inserted as the PC value in all initial thread contexts. +// It does some housekeeping and then calls the real entry point. + +void +Cyg_HardwareThread::thread_entry( Cyg_Thread *thread ) +{ + CYG_REPORT_FUNCTION(); + + Cyg_Scheduler::scheduler.need_reschedule = false; // finished rescheduling + Cyg_Scheduler::scheduler.current_thread = thread; // restore current thread pointer + +#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE + // Reset the timeslice counter so that this thread gets a full + // quantum. + Cyg_Scheduler::reset_timeslice_count(); +#endif + + // Zero the lock + Cyg_Scheduler::sched_lock = 0; + + // Call entry point in a loop. + + for(;;) + { + thread->entry_point(thread->entry_data); + thread->exit(); + } +} + +// ========================================================================= +// Cyg_Thread members + +// ------------------------------------------------------------------------- +// Statics and thread list functions + +#ifdef CYGVAR_KERNEL_THREADS_LIST + +// List of all extant threads +Cyg_Thread *Cyg_Thread::thread_list = 0; + +inline void +Cyg_Thread::add_to_list( void ) +{ + // Add thread to housekeeping list + Cyg_Scheduler::lock(); + + if( thread_list == 0 ) + list_next = this; + else { + Cyg_Thread *prev = thread_list; + do { + if ( this == prev ) + break; // found it already! + prev = prev->list_next; + } while ( prev != thread_list ); + if ( this != prev ) { + // insert it in the list: + list_next = thread_list->list_next; + thread_list->list_next = this; + } + } + thread_list = this; + + Cyg_Scheduler::unlock(); +} + +inline void +Cyg_Thread::remove_from_list( void ) +{ + // remove thread from housekeeping list + Cyg_Scheduler::lock(); + + Cyg_Thread *prev = thread_list; + + do { + if( prev->list_next == this ) { + prev->list_next = list_next; + if( thread_list == this ) + thread_list = list_next; + break; + } + prev = prev->list_next; + } while ( prev != thread_list ); + + Cyg_Scheduler::unlock(); +} + +#endif + +static cyg_uint16 next_unique_id = 1; + +// ------------------------------------------------------------------------- +// Magic new operator to allow the thread constructor to be +// recalled. + +inline void * +operator new(size_t size, Cyg_Thread *ptr) +{ return (void *)ptr; }; + +// Constructor +// This constructor is deprecated and should be replaced with the new +// one below. Until that happens, any changes must be made to both +// constructors. + +Cyg_Thread::Cyg_Thread( + cyg_thread_entry *entry, // entry point function + CYG_ADDRWORD entry_data, // entry data + cyg_ucount32 stack_size, // stack size, 0 = use default + CYG_ADDRESS stack_base // stack base, NULL = allocate +) +: Cyg_HardwareThread(entry, entry_data, stack_size, stack_base), + Cyg_SchedThread(this, CYG_SCHED_DEFAULT_INFO) +#ifdef CYGFUN_KERNEL_THREADS_TIMER + ,timer(this) +#endif +{ + CYG_REPORT_FUNCTION(); + + // Start the thread in suspended state. + state = SUSPENDED; + suspend_count = 1; + + // Initialize sleep_reason which is used by kill, release + sleep_reason = NONE; + wake_reason = NONE; + + // Assign a 16 bit id to the thread. + unique_id = next_unique_id++; + +#ifdef CYGVAR_KERNEL_THREADS_DATA + // Zero all per-thread data entries. + for( int i = 0; i < CYGNUM_KERNEL_THREADS_DATA_MAX; i++ ) + thread_data[i] = 0; +#endif +#ifdef CYGVAR_KERNEL_THREADS_NAME + name = 0; +#endif +#ifdef CYGVAR_KERNEL_THREADS_LIST + // Add thread to housekeeping list + add_to_list(); +#endif + + Cyg_Scheduler::scheduler.register_thread(this); + + init_context(this); + + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Constructor +// This is an alternative constructor that will become the default +// eventually. + +Cyg_Thread::Cyg_Thread( + CYG_ADDRWORD sched_info, // Scheduling parameter(s) + cyg_thread_entry *entry, // entry point function + CYG_ADDRWORD entry_data, // entry data + char *name_arg, // thread name cookie + CYG_ADDRESS stack_base, // stack base, NULL = allocate + cyg_ucount32 stack_size // stack size, 0 = use default + ) +: Cyg_HardwareThread(entry, entry_data, stack_size, stack_base), + Cyg_SchedThread(this, sched_info) +#ifdef CYGFUN_KERNEL_THREADS_TIMER + ,timer(this) +#endif +{ + CYG_REPORT_FUNCTION(); + + // Start the thread in suspended state. + state = SUSPENDED; + suspend_count = 1; + + // Initialize sleep_reason which is used by kill, release + sleep_reason = NONE; + wake_reason = NONE; + + // Assign a 16 bit id to the thread. + unique_id = next_unique_id++; + +#ifdef CYGVAR_KERNEL_THREADS_DATA + // Zero all per-thread data entries. + for( int i = 0; i < CYGNUM_KERNEL_THREADS_DATA_MAX; i++ ) + thread_data[i] = 0; +#endif +#ifdef CYGVAR_KERNEL_THREADS_NAME + name = name_arg; +#endif +#ifdef CYGVAR_KERNEL_THREADS_LIST + // Add thread to housekeeping list + add_to_list(); +#endif + + Cyg_Scheduler::scheduler.register_thread(this); + + init_context(this); + + CYG_REPORT_RETURN(); +} + + +// ------------------------------------------------------------------------- +// Re-initialize this thread. +// We do this by re-invoking the constructor with the original +// arguments, which are still available in the object. + +void +Cyg_Thread::reinitialize() +{ + CYG_REPORT_FUNCTION(); + + CYG_ASSERTCLASS( this, "Bad thread"); + CYG_ASSERT( this != Cyg_Scheduler::get_current_thread(), + "Attempt to reinitialize current thread"); + CYG_ASSERT( get_current_queue() == NULL , "Thread is still on a queue"); + +#ifdef CYGFUN_KERNEL_THREADS_TIMER + // Clear the timeout. It is irrelevant whether there was + // actually a timeout pending. + timer.disable(); +#endif + + // Ensure the scheduler has let go of us. + Cyg_Scheduler::scheduler.deregister_thread(this); + + cyg_priority pri = get_priority(); +#ifdef CYGVAR_KERNEL_THREADS_NAME + char * name_arg = name; +#else + char * name_arg = NULL; +#endif + + new(this) Cyg_Thread( pri, + entry_point, entry_data, + name_arg, + stack_base, stack_size ); + // the constructor re-registers the thread with the scheduler. + + CYG_ASSERTCLASS( this, "Thread corrupted by reinitialize"); + + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Destructor. + +Cyg_Thread::~Cyg_Thread() +{ + CYG_REPORT_FUNCTION(); + + Cyg_Scheduler::scheduler.deregister_thread(this); + +#ifdef CYGVAR_KERNEL_THREADS_LIST + // Remove thread from housekeeping list. + remove_from_list(); +#endif + + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Thread consistency checker. + +#ifdef CYGDBG_USE_ASSERTS + +bool +Cyg_Thread::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( (state & SUSPENDED) && (suspend_count == 0) ) return false; + case cyg_quick: + case cyg_trivial: + case cyg_none: + default: + break; + }; + + return true; +} + +#endif + +// ------------------------------------------------------------------------- +// Put the thread to sleep. +// This can only be called by the current thread on itself, hence +// it is a static function. + +void +Cyg_Thread::sleep() +{ + CYG_REPORT_FUNCTION(); + + Cyg_Thread *current = Cyg_Scheduler::get_current_thread(); + + CYG_ASSERTCLASS( current, "Bad current thread" ); + + CYG_INSTRUMENT_THREAD(SLEEP,current,0); + + // Prevent preemption + Cyg_Scheduler::lock(); + + // If running, remove from run qs + if ( current->state == RUNNING ) + Cyg_Scheduler::scheduler.rem_thread(current); + + // Set the state + current->state |= SLEEPING; + + // Unlock the scheduler and switch threads + Cyg_Scheduler::unlock(); + + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Awaken the thread from sleep. + +void +Cyg_Thread::wake() +{ + CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_THREAD(WAKE,this,Cyg_Scheduler::current_thread); + + // Prevent preemption + Cyg_Scheduler::lock(); + + if( 0 != (state & SLEEPSET) ) + { + // Set the state + state &= ~SLEEPSET; + + // remove from any queue we were on + remove(); + + // If the thread is now runnable, return it to run queue + if( state == RUNNING ) + Cyg_Scheduler::scheduler.add_thread(this); + + } + + // Unlock the scheduler and maybe switch threads + Cyg_Scheduler::unlock(); + + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Put the thread to sleep, with wakeup count. +// This can only be called by the current thread on itself, hence +// it is a static function. + +void +Cyg_Thread::counted_sleep() +{ + CYG_REPORT_FUNCTION(); + + Cyg_Thread *current = Cyg_Scheduler::get_current_thread(); + + CYG_ASSERTCLASS( current, "Bad current thread" ); + + CYG_INSTRUMENT_THREAD(SLEEP,current,0); + + // Prevent preemption + Cyg_Scheduler::lock(); + + if ( 0 == current->wakeup_count ) { + set_sleep_reason( Cyg_Thread::WAIT ); + current->sleep(); // prepare to sleep + current->state |= COUNTSLEEP; // Set the state + } + else + // there is a queued wakeup, do not sleep + current->wakeup_count--; + + // Unlock the scheduler and switch threads + Cyg_Scheduler::unlock(); + + // and deal with anything we must do when we return + switch( current->wake_reason ) { + case DESTRUCT: + case EXIT: + current->exit(); + break; + + default: + break; + } + + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Put the thread to sleep for a delay, with wakeup count. +// This can only be called by the current thread on itself, hence +// it is a static function. + +#ifdef CYGFUN_KERNEL_THREADS_TIMER +void +Cyg_Thread::counted_sleep( cyg_tick_count delay ) +{ + CYG_REPORT_FUNCTION(); + + Cyg_Thread *current = Cyg_Scheduler::get_current_thread(); + + CYG_ASSERTCLASS( current, "Bad current thread" ); + + CYG_INSTRUMENT_THREAD(SLEEP,current,0); + + // Prevent preemption + Cyg_Scheduler::lock(); + + if ( 0 == current->wakeup_count ) { + + // Set the timer (once outside any waiting loop.) + set_timer( Cyg_Clock::real_time_clock->current_value()+delay, + Cyg_Thread::TIMEOUT ); + + // If the timeout is in the past, the wake reason will have been + // set to something other than NONE already. + + if( current->get_wake_reason() == Cyg_Thread::NONE ) + { + set_sleep_reason( Cyg_Thread::TIMEOUT ); + current->sleep(); // prepare to sleep + current->state |= COUNTSLEEP; // Set the state + + CYG_ASSERT( Cyg_Scheduler::get_sched_lock() == 1, + "Called with non-zero scheduler lock"); + + Cyg_Scheduler::unlock(); + Cyg_Scheduler::lock(); + + // clear the timer; if it actually fired, no worries. + clear_timer(); + } + } + else + // there is a queued wakeup, do not sleep + current->wakeup_count--; + + // Unlock the scheduler and switch threads + Cyg_Scheduler::unlock(); + + // and deal with anything we must do when we return + switch( current->wake_reason ) { + case DESTRUCT: + case EXIT: + current->exit(); + break; + + default: + break; + } + + CYG_REPORT_RETURN(); +} +#endif + +// ------------------------------------------------------------------------- +// Awaken the thread from sleep. + +void +Cyg_Thread::counted_wake() +{ + CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_THREAD(WAKE,this,Cyg_Scheduler::current_thread); + + // Prevent preemption + Cyg_Scheduler::lock(); + + if ( 0 == (state & COUNTSLEEP) ) // already awake, or waiting: + wakeup_count++; // not in a counted sleep anyway. + else { + sleep_reason = NONE; + wake_reason = DONE; + wake(); // and awaken the thread + } + +#ifdef CYGNUM_KERNEL_MAX_COUNTED_WAKE_COUNT_ASSERT + CYG_ASSERT( CYGNUM_KERNEL_MAX_COUNTED_WAKE_COUNT_ASSERT > wakeup_count, + "wakeup_count overflow" ); +#endif + + // Unlock the scheduler and maybe switch threads + Cyg_Scheduler::unlock(); + + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Cancel wakeups for this thread and return how many were pending +cyg_uint32 +Cyg_Thread::cancel_counted_wake() +{ + CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_THREAD(WAKE,this,Cyg_Scheduler::current_thread); + + // Prevent preemption + Cyg_Scheduler::lock(); + + cyg_uint32 result = wakeup_count; + wakeup_count = 0; + + // Unlock the scheduler + Cyg_Scheduler::unlock(); + + CYG_REPORT_RETVAL( result ); + return result; +} + +// ------------------------------------------------------------------------- +// Suspend thread. Increment suspend count and deschedule thread +// if still running. + +void +Cyg_Thread::suspend() +{ + CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_THREAD(SUSPEND,this,Cyg_Scheduler::current_thread); + + // Prevent preemption + Cyg_Scheduler::lock(); + + suspend_count++; + +#ifdef CYGNUM_KERNEL_MAX_SUSPEND_COUNT_ASSERT + CYG_ASSERT( CYGNUM_KERNEL_MAX_SUSPEND_COUNT_ASSERT > suspend_count, + "suspend_count overflow" ); +#endif + + // If running, remove from run qs + if( state == RUNNING ) + Cyg_Scheduler::scheduler.rem_thread(this); + + // Set the state + state |= SUSPENDED; + + // Unlock the scheduler and maybe switch threads + Cyg_Scheduler::unlock(); + + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Resume thread. Decrement suspend count and reschedule if it +// is zero. + +void +Cyg_Thread::resume() +{ + CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_THREAD(RESUME,this,Cyg_Scheduler::current_thread); + + // Prevent preemption + Cyg_Scheduler::lock(); + + // If we are about to zero the count, clear the state bit and + // reschedule the thread if possible. + + if( suspend_count == 1 ) + { + suspend_count = 0; + + CYG_ASSERT( (state & SUSPENDED) != 0, "SUSPENDED bit not set" ); + + // Set the state + state &= ~SUSPENDED; + + // Return thread to scheduler if runnable + if( state == RUNNING ) + Cyg_Scheduler::scheduler.add_thread(this); + } + else + if( suspend_count > 0 ) + suspend_count--; + // else ignore attempt to resume + + // Unlock the scheduler and maybe switch threads + Cyg_Scheduler::unlock(); + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Forced Resume thread. Zero suspend count and reschedule... + +void +Cyg_Thread::force_resume() +{ + CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_THREAD(RESUME,this,Cyg_Scheduler::current_thread); + + // Prevent preemption + Cyg_Scheduler::lock(); + + // If we are about to zero the count, clear the state bit and + // reschedule the thread if possible. + + if ( 0 < suspend_count ) { + suspend_count = 0; + + CYG_ASSERT( (state & SUSPENDED) != 0, "SUSPENDED bit not set" ); + + // Set the state + state &= ~SUSPENDED; + + // Return thread to scheduler if runnable + if( state == RUNNING ) + Cyg_Scheduler::scheduler.add_thread(this); + } + + // Unlock the scheduler and maybe switch threads + Cyg_Scheduler::unlock(); + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Force thread to wake up from a sleep with a wake_reason of +// BREAK. It is the responsibility of the woken thread to detect +// the release() and do the right thing. + +void +Cyg_Thread::release() +{ + CYG_REPORT_FUNCTION(); + // Prevent preemption + Cyg_Scheduler::lock(); + + // If the thread is in any of the sleep states, set the + // wake reason and wake it up. + + switch( sleep_reason ) + { + + case NONE: + // The thread is not sleeping for any reason, do nothing. + // drop through... + + case DESTRUCT: + case BREAK: + case EXIT: + case DONE: + // Do nothing in any of these cases. They are here to + // keep the compiler happy. + + Cyg_Scheduler::unlock(); + CYG_REPORT_RETURN(); + return; + + case WAIT: + // The thread was waiting for some sync object to do + // something. + // drop through... + + case TIMEOUT: + // The thread was waiting on a sync object with a timeout. + // drop through... + + case DELAY: + // The thread was simply delaying, unless it has been + // woken up for some other reason, wake it now. + sleep_reason = NONE; + wake_reason = BREAK; + break; + } + + wake(); + + // Allow preemption + Cyg_Scheduler::unlock(); + + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Exit thread. This puts the thread into EXITED state. + +void +Cyg_Thread::exit() +{ + // The thread should never return from this function. + + Cyg_Thread *self = Cyg_Thread::self(); + + Cyg_Scheduler::lock(); + + // clear the timer; if there was none, no worries. + clear_timer(); + + self->state = EXITED; + + Cyg_Scheduler::scheduler.rem_thread(self); + + // Un-nest any scheduler locks we have until we + // suspend. + for( ;; ) Cyg_Scheduler::unlock(); +} + +// ------------------------------------------------------------------------- +// Kill thread. Force the thread into EXITED state externally, or +// make it wake up and call exit(). + +void +Cyg_Thread::kill() +{ + CYG_REPORT_FUNCTION(); + // If this is called by the current thread on itself, + // just call exit(), which is what he should have done + // in the first place. + if( this == Cyg_Scheduler::get_current_thread() ) + exit(); + + // Prevent preemption + Cyg_Scheduler::lock(); + + // We are killing someone else. Find out what state he is + // in and force him to wakeup and call exit(). + + force_resume(); // this is necessary for when + // he is asleep AND suspended. +#ifdef CYGFUN_KERNEL_THREADS_TIMER + timer.disable(); // and make sure the timer + // does not persist. +#endif + + switch( sleep_reason ) + { + + case NONE: + // The thread is not sleeping for any reason, it must be + // on a run queue. + // We can safely deschedule and set its state. + if( state == RUNNING ) Cyg_Scheduler::scheduler.rem_thread(this); + state = EXITED; + break; + + case DESTRUCT: + case BREAK: + case EXIT: + case DONE: + // Do nothing in any of these cases. They are here to + // keep the compiler happy. + + Cyg_Scheduler::unlock(); + CYG_REPORT_RETURN(); + return; + + case WAIT: + // The thread was waiting for some sync object to do + // something. + // drop through... + + case TIMEOUT: + // The thread was waiting on a sync object with a timeout. + // drop through... + + case DELAY: + // The thread was simply delaying, unless it has been + // woken up for some other reason, wake it now. + sleep_reason = NONE; + wake_reason = EXIT; + break; + } + + wake(); + + // Allow preemption + Cyg_Scheduler::unlock(); + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Set thread priority + +#ifdef CYGIMP_THREAD_PRIORITY + +void +Cyg_Thread::set_priority( cyg_priority new_priority ) +{ + CYG_REPORT_FUNCTION(); + +// CYG_ASSERT( new_priority >= CYG_THREAD_MAX_PRIORITY, "Priority out of range"); +// CYG_ASSERT( new_priority <= CYG_THREAD_MIN_PRIORITY, "Priority out of range"); + + CYG_INSTRUMENT_THREAD(PRIORITY,this,priority); + + // Prevent preemption + Cyg_Scheduler::lock(); + + Cyg_ThreadQueue *queue = NULL; + + // If running, remove from run qs + if( state == RUNNING ) + Cyg_Scheduler::scheduler.rem_thread(this); + else if( state & SLEEPING ) + { + // Remove thread from current queue. + queue = get_current_queue(); + // if indeed we are on a queue + if ( NULL != queue ) { + CYG_CHECK_DATA_PTR(queue, "Bad queue pointer"); + remove(); + } + } + + Cyg_Scheduler::scheduler.deregister_thread(this); + +#if CYG_SCHED_UNIQUE_PRIORITIES + + // Check that there are no other threads at this priority. + // If so, leave is as it is. + + CYG_ASSERT( Cyg_Scheduler::scheduler.unique(new_priority), "Priority not unique"); + + if( Cyg_Scheduler::scheduler.unique(new_priority) ) + priority = new_priority; + +#else // !CYG_SCHED_UNIQUE_PRIORITIES + +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE_SIMPLE + + // When we have priority inheritance, we must update the original + // priority and not the inherited one. If the new priority is + // better than the current inherited one, then use that + // immediately. We remain in inherited state to avoid problems + // with multiple mutex inheritances. + + if( priority_inherited ) + { + original_priority = new_priority; + if( priority > new_priority ) priority = new_priority; + } + else priority = new_priority; + +#else + + priority = new_priority; + +#endif + +#endif // CYG_SCHED_UNIQUE_PRIORITIES + + Cyg_Scheduler::scheduler.register_thread(this); + + // Return thread to scheduler if runnable + if( state == RUNNING ) + Cyg_Scheduler::scheduler.add_thread(this); + else if ( state & SLEEPING ) + { + // return to current queue + // if indeed we are on a queue + if ( NULL != queue ) { + CYG_CHECK_DATA_PTR(queue, "Bad queue pointer"); + queue->enqueue(this); + } + } + + // If the current thread is being reprioritized, set the + // reschedule flag to ensure that it gets rescheduled if + // necessary. (Strictly we only need to do this if the new + // priority is less than that of some other runnable thread, in + // practice checking that is as expensive as what the scheduler + // will do anyway). + + if( this == Cyg_Scheduler::get_current_thread() ) + Cyg_Scheduler::need_reschedule = true; + + // Unlock the scheduler and maybe switch threads + Cyg_Scheduler::unlock(); + CYG_REPORT_RETURN(); +} + +#endif + + +// ------------------------------------------------------------------------- +// Thread delay function + +void +Cyg_Thread::delay( cyg_tick_count delay) +{ + CYG_REPORT_FUNCTION(); + +#if defined(CYGFUN_KERNEL_THREADS_TIMER) && defined(CYGVAR_KERNEL_COUNTERS_CLOCK) + + CYG_INSTRUMENT_THREAD(DELAY,this,delay); + + // Prevent preemption + Cyg_Scheduler::lock(); + + sleep(); + + set_timer( Cyg_Clock::real_time_clock->current_value()+delay, DELAY ); + + // Unlock the scheduler and maybe switch threads + Cyg_Scheduler::unlock(); + + // Clear the timeout. It is irrelevant whether the alarm has + // actually gone off or not. + clear_timer(); + + // and deal with anything else we must do when we return + switch( wake_reason ) { + case DESTRUCT: + case EXIT: + exit(); + break; + + default: + break; + } +#endif + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// + +#ifdef CYGPKG_KERNEL_EXCEPTIONS + +void +Cyg_Thread::deliver_exception( + cyg_code exception_number, // exception being raised + CYG_ADDRWORD exception_info // exception specific info + ) +{ + if( this == Cyg_Scheduler::get_current_thread() ) + { + // Delivering to current thread, probably as a result + // of a real hardware exception. Simply invoke the appropriate + // handler. + + exception_control.deliver_exception( exception_number, exception_info ); + } +#ifdef CYGIMP_EXCEPTION_ASYNC + else + { + // Delivering to another thread, probably as a result of one thread + // invoking this function on another thread. Adjust the other thread's + // state to make it execute the exception routine when it next runs. + + // At present there is an unresolved problem here. We do not know what + // state the destination thread is in. It may not be a suitable point at + // which to invoke an exception routine. In most cases the exception + // routine will be run in the scheduler thread switch code, where the world is + // in an inconsistent state. We really need to run the routine at the + // end of unlock_inner(). However this would add extra code to the scheduler, + // and require a way of storing pending exceptions. So for now this option is + // disabled and not yet implemented, it may never be. + + } +#endif +} + +#endif + +// ------------------------------------------------------------------------- +// Per-thread data support + +#ifdef CYGVAR_KERNEL_THREADS_DATA + +// Set the data map bits for each free slot in the data array. +cyg_ucount32 Cyg_Thread::thread_data_map = (~CYGNUM_KERNEL_THREADS_DATA_ALL) & + ((1<<CYGNUM_KERNEL_THREADS_DATA_MAX)-1); + +cyg_ucount32 +Cyg_Thread::new_data_index() +{ + Cyg_Scheduler::lock(); + + cyg_ucount32 index; + + CYG_ASSERT( thread_data_map != 0 , "No more thread data indexes"); + + // find ls set bit + HAL_LSBIT_INDEX( index, thread_data_map ); + + // clear the bit + thread_data_map &= ~(1<<index); + + Cyg_Scheduler::unlock(); + + return index; +} + +void Cyg_Thread::free_data_index( cyg_ucount32 index ) +{ + Cyg_Scheduler::lock(); + + thread_data_map |= (1<<index); + + Cyg_Scheduler::unlock(); +} + + +#endif + +// ========================================================================= +// Cyg_ThreadTimer member functions + +// ------------------------------------------------------------------------- +// Timer alarm function. Inspect the sleep_reason and if necessary wake +// up the thread with an appropriate wake_reason. + +#ifdef CYGFUN_KERNEL_THREADS_TIMER + +void +Cyg_ThreadTimer::alarm( + Cyg_Alarm *alarm, + CYG_ADDRWORD data +) +{ + CYG_REPORT_FUNCTION(); + + Cyg_ThreadTimer *self = (Cyg_ThreadTimer *)data; + Cyg_Thread *thread = self->thread; + + CYG_INSTRUMENT_THREAD(ALARM, 0, 0); + + Cyg_Scheduler::lock(); + + Cyg_Thread::cyg_reason sleep_reason = thread->get_sleep_reason(); + + switch( sleep_reason ) { + + case Cyg_Thread::DESTRUCT: + case Cyg_Thread::BREAK: + case Cyg_Thread::EXIT: + case Cyg_Thread::NONE: + case Cyg_Thread::WAIT: + case Cyg_Thread::DONE: + // Do nothing in any of these cases. Most are here to + // keep the compiler happy. + Cyg_Scheduler::unlock(); + CYG_REPORT_RETURN(); + return; + + case Cyg_Thread::DELAY: + // The thread was simply delaying, unless it has been + // woken up for some other reason, wake it now. + thread->set_wake_reason(Cyg_Thread::DONE); + break; + + case Cyg_Thread::TIMEOUT: + // The thread has timed out, set the wake reason to + // TIMEOUT and restart. + thread->set_wake_reason(Cyg_Thread::TIMEOUT); + break; + } + + thread->wake(); + + Cyg_Scheduler::unlock(); + CYG_REPORT_RETURN(); +} + +#endif + +// ========================================================================= +// The Idle thread +// The idle thread is implemented as a single instance of the +// Cyg_IdleThread class. This is so that it can be initialized before +// main in a static constructor. + +// ------------------------------------------------------------------------- +// Data definitions + +// stack + +#ifdef CYG_HAL_MIPS +extern char idle_thread_stack[CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE]; +#else +char idle_thread_stack[CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE]; +#endif + +// Loop counter for debugging/housekeeping +cyg_uint32 idle_thread_loops = 1; + +// ------------------------------------------------------------------------- +// Idle thread code. + +void +idle_thread_main( CYG_ADDRESS data ) +{ + CYG_REPORT_FUNCTION(); + + for(;;) + { + idle_thread_loops++; + + HAL_IDLE_THREAD_ACTION(idle_thread_loops); + +#if 0 + // For testing, it is useful to be able to fake + // clock interrupts in the idle thread. + + Cyg_Clock::real_time_clock->tick(); +#endif +#ifdef CYGIMP_IDLE_THREAD_YIELD + // In single priority and non-preemptive systems, + // the idle thread should yield repeatedly to + // other threads. + Cyg_Thread::yield(); +#endif + } +} + +// ------------------------------------------------------------------------- +// Idle thread class + +class Cyg_IdleThread : public Cyg_Thread +{ +public: + Cyg_IdleThread( + cyg_thread_entry *entry, // entry point function + CYG_ADDRWORD entry_data, // entry data + cyg_ucount32 stack_size = 0, // stack size, 0 = use default + CYG_ADDRESS stack_base = 0 // stack base, NULL = allocate + ); + +}; + +// ------------------------------------------------------------------------- +// Idle threads constructor + +Cyg_IdleThread::Cyg_IdleThread( + cyg_thread_entry *entry, // entry point function + CYG_ADDRWORD entry_data, // entry data + cyg_ucount32 stack_size, // stack size, 0 = use default + CYG_ADDRESS stack_base // stack base, NULL = allocate + ) + : Cyg_Thread( CYG_THREAD_MIN_PRIORITY, + entry, + entry_data, + "Idle Thread", + stack_base, + stack_size) +{ + CYG_REPORT_FUNCTION(); + + resume(); + CYG_REPORT_RETURN(); +} + +// ------------------------------------------------------------------------- +// Instantiate the idle thread + +Cyg_IdleThread idle_thread CYG_INIT_PRIORITY( IDLE_THREAD ) = +Cyg_IdleThread( idle_thread_main, + 0, + CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE, + CYG_ADDRESS(idle_thread_stack) + ); + + +// ------------------------------------------------------------------------- +// EOF common/thread.cxx
