Mercurial > ecos-v3_0-branch
diff packages/kernel/current/src/sync/mutex.cxx @ 115:6ed91473a1cd ecos-sw-2000-08-21
Merge from eCos master repository on 2000-08-21-22:40:54-BST
| author | jlarmour |
|---|---|
| date | Fri, 25 Aug 2000 17:32:38 +0000 |
| parents | 59d97b6ba612 |
| children | 0ec04793409a |
line wrap: on
line diff
--- a/packages/kernel/current/src/sync/mutex.cxx +++ b/packages/kernel/current/src/sync/mutex.cxx @@ -55,6 +55,23 @@ #include <cyg/kernel/sched.inl> // scheduler inlines #include <cyg/kernel/clock.inl> // clock inlines +// ------------------------------------------------------------------------- +// Mutex protocol test macros. +// If the dynamic protocol option is enabled, then these generate appropriate +// tests on the protocol field. If there is no dynamic choice then they simply +// result in empty statements. + +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DYNAMIC + +#define IF_PROTOCOL_INHERIT if( protocol == INHERIT ) +#define IF_PROTOCOL_CEILING if( protocol == CEILING ) + +#else + +#define IF_PROTOCOL_INHERIT +#define IF_PROTOCOL_CEILING + +#endif // ------------------------------------------------------------------------- // Constructor @@ -66,10 +83,60 @@ Cyg_Mutex::Cyg_Mutex() locked = false; owner = NULL; +#if defined(CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT) && \ + defined(CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DYNAMIC) + +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT_INHERIT + protocol = INHERIT; +#endif +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT_CEILING + protocol = CEILING; + ceiling = CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT_PRIORITY; +#endif +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT_NONE + protocol = NONE; +#endif + +#endif + CYG_REPORT_RETURN(); } // ------------------------------------------------------------------------- +// Construct with defined protocol + +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DYNAMIC + +Cyg_Mutex::Cyg_Mutex( cyg_protcol protocol_arg ) +{ + CYG_REPORT_FUNCTION(); + + locked = false; + owner = NULL; + + protocol = protocol_arg; + +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT_PRIORITY + + // if there is a default priority ceiling defined, use that to initialize + // the ceiling. + ceiling = CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT_PRIORITY; + +#else + + // Otherwise set it to zero. + ceiling = 0; + +#endif +#endif + + CYG_REPORT_RETURN(); +} + +#endif + +// ------------------------------------------------------------------------- // Destructor Cyg_Mutex::~Cyg_Mutex() @@ -135,7 +202,7 @@ Cyg_Mutex::lock(void) // thread grabbing the mutex between the wakeup in unlock() and // this thread actually starting. -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL self->count_mutex(); @@ -151,21 +218,18 @@ Cyg_Mutex::lock(void) queue.enqueue( self ); -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT - owner->inherit_priority(self); + IF_PROTOCOL_INHERIT + owner->inherit_priority(self); #endif CYG_INSTRUMENT_MUTEX(WAIT, this, 0); - CYG_ASSERT( Cyg_Scheduler::get_sched_lock() == 1, "Called with non-zero scheduler lock"); + // Allow other threads to run + Cyg_Scheduler::reschedule(); - // Unlock scheduler and allow other threads - // to run - Cyg_Scheduler::unlock(); - Cyg_Scheduler::lock(); - CYG_ASSERTCLASS( this, "Bad this pointer"); switch( self->get_wake_reason() ) @@ -190,13 +254,23 @@ Cyg_Mutex::lock(void) locked = true; owner = self; +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING + + IF_PROTOCOL_CEILING + self->set_priority_ceiling(ceiling); + +#endif + CYG_INSTRUMENT_MUTEX(LOCKED, this, 0); } -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT else { - self->uncount_mutex(); - self->disinherit_priority(); + IF_PROTOCOL_INHERIT + { + self->uncount_mutex(); + self->disinherit_priority(); + } } #endif @@ -234,11 +308,18 @@ Cyg_Mutex::trylock(void) locked = true; owner = self; -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL self->count_mutex(); #endif +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING + + IF_PROTOCOL_CEILING + self->set_priority_ceiling(ceiling); + +#endif + } else result = false; @@ -277,12 +358,13 @@ Cyg_Mutex::unlock(void) CYG_ASSERTCLASS( thread, "Bad thread pointer"); -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT // Give the owner-to-be a chance to inherit from the remaining // queue or the relinquishing thread: - thread->relay_priority(owner, &queue); + IF_PROTOCOL_INHERIT + thread->relay_priority(owner, &queue); #endif @@ -294,12 +376,23 @@ Cyg_Mutex::unlock(void) } -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL owner->uncount_mutex(); - owner->disinherit_priority(); + +#endif +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT + + IF_PROTOCOL_INHERIT + owner->disinherit_priority(); #endif +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING + + IF_PROTOCOL_CEILING + owner->clear_priority_ceiling(); + +#endif locked = false; owner = NULL; @@ -349,6 +442,31 @@ void Cyg_Mutex::release() CYG_REPORT_RETURN(); } +// ------------------------------------------------------------------------- +// Set ceiling priority for priority ceiling protocol + +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING + +void Cyg_Mutex::set_ceiling( cyg_priority priority ) +{ + CYG_REPORT_FUNCTION(); + +// CYG_ASSERT( priority >= CYG_THREAD_MAX_PRIORITY, "Priority out of range"); +// CYG_ASSERT( priority <= CYG_THREAD_MIN_PRIORITY, "Priority out of range"); + + // Prevent preemption + Cyg_Scheduler::lock(); + + ceiling = priority; + + // Unlock the scheduler + Cyg_Scheduler::unlock(); + + CYG_REPORT_RETURN(); +} + +#endif + //========================================================================== // Condition variables @@ -365,6 +483,15 @@ Cyg_Condition_Variable::Cyg_Condition_Va CYG_REPORT_RETURN(); } +Cyg_Condition_Variable::Cyg_Condition_Variable() +{ + CYG_REPORT_FUNCTION(); + + mutex = NULL; + + CYG_REPORT_RETURN(); +} + // ------------------------------------------------------------------------- // Destructor @@ -399,7 +526,7 @@ Cyg_Condition_Variable::check_this( cyg_ case cyg_system_test: case cyg_extreme: case cyg_thorough: - if( !mutex->check_this(zeal) ) + if( mutex != NULL && !mutex->check_this(zeal) ) result = false; case cyg_quick: case cyg_trivial: @@ -422,11 +549,12 @@ Cyg_Condition_Variable::check_this( cyg_ // in this case, exit with the scheduler locked, which allows this function // to be used in a totally thread-safe manner. -void -Cyg_Condition_Variable::wait(void) +cyg_bool +Cyg_Condition_Variable::wait_inner( Cyg_Mutex *mx ) { CYG_REPORT_FUNCTION(); - + + cyg_bool result = true; Cyg_Thread *self = Cyg_Thread::self(); cyg_int32 current_lock = Cyg_Scheduler::get_sched_lock(); @@ -436,12 +564,12 @@ Cyg_Condition_Variable::wait(void) Cyg_Scheduler::lock(); CYG_ASSERTCLASS( this, "Bad this pointer"); - CYG_ASSERTCLASS( mutex, "Corrupt mutex"); + CYG_ASSERTCLASS( mx, "Corrupt mutex"); CYG_ASSERTCLASS( self, "Bad self thread"); CYG_INSTRUMENT_CONDVAR(WAIT, this, 0); - mutex->unlock(); + mx->unlock(); self->set_sleep_reason( Cyg_Thread::WAIT ); @@ -450,17 +578,28 @@ Cyg_Condition_Variable::wait(void) queue.enqueue( self ); CYG_ASSERT( Cyg_Scheduler::get_sched_lock() == 1, "Called with non-zero scheduler lock"); + + // Avoid calling ASRs during the following unlock. + self->set_asr_inhibit(); // Unlock the scheduler and switch threads Cyg_Scheduler::unlock(); + // Allow ASRs again + self->clear_asr_inhibit(); + CYG_INSTRUMENT_CONDVAR(WOKE, this, self->get_wake_reason()); - + CYG_ASSERTCLASS( this, "Bad this pointer"); - CYG_ASSERTCLASS( mutex, "Corrupt mutex"); + CYG_ASSERTCLASS( mx, "Corrupt mutex"); switch( self->get_wake_reason() ) { + case Cyg_Thread::DESTRUCT: // which, the cv or the mutex? + case Cyg_Thread::BREAK: + result = false; + break; + case Cyg_Thread::EXIT: self->exit(); break; @@ -469,7 +608,6 @@ Cyg_Condition_Variable::wait(void) break; } - // When we awake, we must re-acquire the mutex. Note that while // it is essential to release the mutex and queue on the CV // atomically relative to other threads, to avoid races, it is not @@ -478,19 +616,21 @@ Cyg_Condition_Variable::wait(void) // We need to loop here in case the thread is released while waiting // for the mutex. It is essential that we exit this function with the // mutex claimed. - - while ( !mutex->lock() ) + + while ( !mx->lock() ) continue; CYG_ASSERTCLASS( this, "Bad this pointer"); - CYG_ASSERTCLASS( mutex, "Corrupt mutex"); - CYG_ASSERT( mutex->owner == self, "Not mutex owner"); + CYG_ASSERTCLASS( mx, "Corrupt mutex"); + CYG_ASSERT( mx->owner == self, "Not mutex owner"); CYG_REPORT_RETURN(); if (current_lock) // Reacquire the DSR pseudo lock Cyg_Scheduler::lock(); + + return result; } // ------------------------------------------------------------------------- @@ -502,7 +642,6 @@ Cyg_Condition_Variable::signal(void) CYG_REPORT_FUNCTION(); CYG_ASSERTCLASS( this, "Bad this pointer"); - CYG_ASSERTCLASS( mutex, "Corrupt mutex"); // Prevent preemption Cyg_Scheduler::lock(); @@ -527,7 +666,6 @@ Cyg_Condition_Variable::signal(void) } CYG_ASSERTCLASS( this, "Bad this pointer"); - CYG_ASSERTCLASS( mutex, "Corrupt mutex"); // Unlock the scheduler and maybe switch threads Cyg_Scheduler::unlock(); @@ -544,7 +682,6 @@ Cyg_Condition_Variable::broadcast(void) CYG_REPORT_FUNCTION(); CYG_ASSERTCLASS( this, "Bad this pointer"); - CYG_ASSERTCLASS( mutex, "Corrupt mutex"); // Prevent preemption Cyg_Scheduler::lock(); @@ -568,7 +705,6 @@ Cyg_Condition_Variable::broadcast(void) } CYG_ASSERTCLASS( this, "Bad this pointer"); - CYG_ASSERTCLASS( mutex, "Corrupt mutex"); // Unlock the scheduler and maybe switch threads Cyg_Scheduler::unlock(); @@ -582,13 +718,13 @@ Cyg_Condition_Variable::broadcast(void) #if defined(CYGMFN_KERNEL_SYNCH_CONDVAR_TIMED_WAIT) cyg_bool -Cyg_Condition_Variable::wait( cyg_tick_count timeout ) +Cyg_Condition_Variable::wait_inner( Cyg_Mutex *mx, cyg_tick_count timeout ) { CYG_REPORT_FUNCTYPE("returning %d"); CYG_REPORT_FUNCARG1("timeout = %d", timeout); CYG_ASSERTCLASS( this, "Bad this pointer"); - CYG_ASSERTCLASS( mutex, "Corrupt mutex"); + CYG_ASSERTCLASS( mx, "Corrupt mutex"); cyg_bool result = true; @@ -601,7 +737,7 @@ Cyg_Condition_Variable::wait( cyg_tick_c CYG_INSTRUMENT_CONDVAR(TIMED_WAIT, this, 0 ); - mutex->unlock(); + mx->unlock(); // The ordering of sleep() and set_timer() here are // important. If the timeout is in the past, the thread @@ -617,12 +753,18 @@ Cyg_Condition_Variable::wait( cyg_tick_c queue.enqueue( self ); CYG_ASSERT( Cyg_Scheduler::get_sched_lock() == 1, "Called with non-zero scheduler lock"); - + + // Avoid calling ASRs during the following unlock. + self->set_asr_inhibit(); + // Unlock the scheduler and switch threads Cyg_Scheduler::unlock(); + // Allow ASRs again + self->clear_asr_inhibit(); + CYG_ASSERTCLASS( this, "Bad this pointer"); - CYG_ASSERTCLASS( mutex, "Corrupt mutex"); + CYG_ASSERTCLASS( mx, "Corrupt mutex"); self->clear_timer(); @@ -651,13 +793,11 @@ Cyg_Condition_Variable::wait( cyg_tick_c // necessary for us to re-acquire the mutex in the same atomic // action. Hence we can do it after unlocking the scheduler. - // FIXME: what if we woke up above due to TIMEOUT/DESTRUCT/BREAK? - // In that situation is it correct to not lock the mutex? - if (false != result) - result = mutex->lock(); - + while ( !mx->lock() ) + continue; + CYG_ASSERTCLASS( this, "Bad this pointer"); - CYG_ASSERTCLASS( mutex, "Corrupt mutex"); + CYG_ASSERTCLASS( mx, "Corrupt mutex"); CYG_REPORT_RETVAL(result);
