📄 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 functionvoidCyg_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_EXCEPTIONSvoidCyg_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_indexCyg_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_TIMERvoidCyg_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_MINIMUMstatic char idle_thread_stack[CYGNUM_KERNEL_CPU_MAX][CYGNUM_KERNEL_THREADS_IDLE_STACK_SIZE];// Loop counter for debugging/housekeepingcyg_uint32 idle_thread_loops[CYGNUM_KERNEL_CPU_MAX];// -------------------------------------------------------------------------// Idle thread code.voididle_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 classclass Cyg_IdleThread : public Cyg_Thread{public: Cyg_IdleThread(); };// -------------------------------------------------------------------------// Instantiate the idle threadCyg_IdleThread idle_thread[CYGNUM_KERNEL_CPU_MAX] CYG_INIT_PRIORITY( IDLE_THREAD );// -------------------------------------------------------------------------// Idle threads constructorCyg_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 + -