📄 thread.cxx
字号:
{
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,new_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 CYGINT_KERNEL_SCHEDULER_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 // !CYGINT_KERNEL_SCHEDULER_UNIQUE_PRIORITIES
#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_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 // CYGINT_KERNEL_SCHEDULER_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 it is not the current thread then we need to see whether
// it is more worthy of execution than any current thread and
// rescheduled if necessary.
if( this == Cyg_Scheduler::get_current_thread() )
Cyg_Scheduler::set_need_reschedule();
else Cyg_Scheduler::set_need_reschedule(this);
// 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();
#ifdef CYGFUN_KERNEL_THREADS_TIMER
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+(((cyg_ucount32)(1<<(CYGNUM_KERNEL_THREADS_DATA_MAX-1))-1)<<1));
// the second expression is equivalent to ((1<<CYGNUM_KERNEL_THREADS_DATA_MAX)-1);
// but avoids overflow. The compiler will compile to a constant just fine.
Cyg_Thread::cyg_data_index
Cyg_Thread::new_data_index()
{
Cyg_Scheduler::lock();
Cyg_Thread::cyg_data_index index;
if (0 == thread_data_map)
return -1;
// 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_Thread::cyg_data_index index )
{
Cyg_Scheduler::lock();
thread_data_map |= (1<<index);
Cyg_Scheduler::unlock();
}
#endif
// -------------------------------------------------------------------------
// Allocate some memory at the lower end of the stack
// by moving the stack limit pointer.
#if defined(CYGFUN_KERNEL_THREADS_STACK_LIMIT) && \ defined(CYGFUN_KERNEL_THREADS_STACK_CHECKING)
// if not doing stack checking, implementation can be found in thread.inl
// This implementation puts the magic buffer area (to watch for overruns
// *above* the stack limit, i.e. there is no official demarcation between
// the stack and the buffer. But that's okay if you think about it... having
// a demarcation would not accomplish anything more.
void *Cyg_HardwareThread::increment_stack_limit( cyg_ucount32 size )
{
void *ret = (void *)stack_limit;
// First lock the scheduler because we're going to be tinkering with
// the check data
Cyg_Scheduler::lock();
// if we've inc'd the limit before, it will be off by the check data
// size, so lets correct it
if (stack_limit != stack_base)
stack_limit -= CYGNUM_KERNEL_THREADS_STACK_CHECK_DATA_SIZE;
stack_limit += size;
// determine base of check data by rounding up to nearest word aligned
// address if not already aligned
cyg_uint32 *p = (cyg_uint32 *)((stack_limit + 3) & ~3);
// i.e. + sizeof(cyg_uint32)-1) & ~(sizeof(cyg_uint32)-1);
cyg_ucount32 i;
cyg_uint32 sig = (cyg_uint32)this;
for ( i = 0;
i < CYGNUM_KERNEL_THREADS_STACK_CHECK_DATA_SIZE/sizeof(cyg_uint32);
i++ ) {
p[i] = (sig ^ (i * 0x01010101));
}
// increment limit by the check size. Note this will not necessarily
// reach the end of the check data. But that doesn't really matter.
// Doing this allows better checking of the saved stack pointer in
// Cyg_Thread::check_this()
stack_limit += CYGNUM_KERNEL_THREADS_STACK_CHECK_DATA_SIZE;
Cyg_Scheduler::unlock();
return ret;
}
#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 CYGNUM_HAL_STACK_SIZE_MINIMUM
# ifdef CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE
# if CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE < CYGNUM_HAL_STACK_SIZE_MINIMUM
// then override the configured stack size
# undef CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE
# define CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE CYGNUM_HAL_STACK_SIZE_MINIMUM
# endif // CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE < CYGNUM_HAL_STACK_SIZE_MINIMUM
# endif // CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE
#endif // CYGNUM_HAL_STACK_SIZE_MINIMUM
static char idle_thread_stack[CYGNUM_KERNEL_CPU_MAX][CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE];
// Loop counter for debugging/housekeeping
cyg_uint32 idle_thread_loops[CYGNUM_KERNEL_CPU_MAX];
// -------------------------------------------------------------------------
// Idle thread code.
void
idle_thread_main( CYG_ADDRESS data )
{
CYG_REPORT_FUNCTION();
for(;;)
{
idle_thread_loops[CYG_KERNEL_CPU_THIS()]++;
HAL_IDLE_THREAD_ACTION(idle_thread_loops[CYG_KERNEL_CPU_THIS()]);
#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();
};
// -------------------------------------------------------------------------
// Instantiate the idle thread
Cyg_IdleThread idle_thread[CYGNUM_KERNEL_CPU_MAX] CYG_INIT_PRIORITY( IDLE_THREAD );
// -------------------------------------------------------------------------
// Idle threads constructor
Cyg_IdleThread::Cyg_IdleThread()
: Cyg_Thread( CYG_THREAD_MIN_PRIORITY,
idle_thread_main,
0,
"Idle Thread",
(CYG_ADDRESS)idle_thread_stack[this-&idle_thread[0]],
CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE)
{
CYG_REPORT_FUNCTION();
// Call into scheduler to set up this thread as the default
// current thread for its CPU.
Cyg_Scheduler::scheduler.set_idle_thread( this, this-&idle_thread[0] );
CYG_REPORT_RETURN();
}
// -------------------------------------------------------------------------
// EOF common/thread.cxx
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -