diff packages/kernel/current/src/sched/sched.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/sched/sched.cxx
+++ b/packages/kernel/current/src/sched/sched.cxx
@@ -58,6 +58,7 @@
 #include <cyg/hal/hal_arch.h>          // Architecture specific definitions
 
 #include <cyg/kernel/thread.inl>       // thread inlines
+#include <cyg/kernel/sched.inl>        // scheduler inlines
 
 //-------------------------------------------------------------------------
 // Some local tracing control - a default.
@@ -91,19 +92,37 @@ cyg_ucount32          Cyg_Scheduler_Base
 
 // -------------------------------------------------------------------------
 // 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()
+// This is only called when the lock is to be zeroed and there is the
+// potential for real work to be done. Other cases are handled in
+// Cyg_Scheduler::unlock() which is an inline.  The new_lock argument
+// contains the value that the scheduler lock should have after this
+// function has completed. If it is zero then the lock is being
+// released and some extra work (running ASRs, checking for DSRs) is
+// done before returning. If it is non-zero then it must equal the
+// current value of the lock, and is used to indicate that we want to
+// reacquire the scheduler lock before returning. This latter option
+// only makes any sense if the current thread is no longer runnable,
+// otherwise this function will do nothing.
+
+void Cyg_Scheduler::unlock_inner( cyg_ucount32 new_lock )
 {
 #ifdef CYGDBG_KERNEL_TRACE_UNLOCK_INNER
     CYG_REPORT_FUNCTION();
 #endif    
 
+    // This assert must be outside the loop because running DSRs can make
+    // it fail if the current thread that was about to sleep is awoken by
+    // the DSR!  Going round the loop to run new DSRs does the same.
+    CYG_ASSERT( (new_lock == 0) ||
+                (current_thread->state != Cyg_Thread::RUNNING ||
+                 need_reschedule) ,
+                "Unnecessary call to unlock_inner()" );
+        
     do {
 
-        CYG_PRECONDITION( sched_lock == 1 , "sched_lock not 1" );
+        CYG_PRECONDITION( new_lock==0 ? sched_lock == 1 : sched_lock == new_lock,
+                          "sched_lock not at expected value" );
         
 #ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS
         
@@ -113,7 +132,7 @@ void Cyg_Scheduler::unlock_inner()
         if( Cyg_Interrupt::DSRs_pending() )
             Cyg_Interrupt::call_pending_DSRs();
 #endif
-            
+
         Cyg_Thread *current = current_thread;
 
         CYG_ASSERTCLASS( current, "Bad current thread" );
@@ -172,29 +191,72 @@ void Cyg_Scheduler::unlock_inner()
 
             need_reschedule = false;        // finished rescheduling
         }
-          
-        HAL_REORDER_BARRIER(); // Make sure everything above has happened
-                               // by this point
-        sched_lock = 0;       // Clear the lock
-        HAL_REORDER_BARRIER();
+
+        if( new_lock == 0 )
+        {
+
+#ifdef CYGSEM_KERNEL_SCHED_ASR_SUPPORT
+
+            // Check whether the ASR is pending and not inhibited.  If
+            // we can call it, then transfer this info to a local
+            // variable (call_asr) and clear the pending flag.  Note
+            // that we only do this if the scheduler lock is about to
+            // be zeroed. In any other circumstance we are not
+            // unlocking.
+
+            cyg_bool call_asr = false;
+            
+            if( !current->asr_inhibit && current->asr_pending )
+            {
+                call_asr = true;
+                current->asr_pending = false;
+            }
+#endif
+            
+            HAL_REORDER_BARRIER(); // Make sure everything above has happened
+                                   // by this point
+            sched_lock = 0;        // Clear the lock
+            HAL_REORDER_BARRIER();
                 
 #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.
+            // 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.
 
-        if( Cyg_Interrupt::DSRs_pending() ) {
-            sched_lock = 1;     // reclaim the lock
-            continue;           // go back to head of loop
-        }
+            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.
+            // Otherwise the lock is zero, we can return.
+
+            CYG_POSTCONDITION( sched_lock == 0, "sched_lock not zero" );
+
+#ifdef CYGSEM_KERNEL_SCHED_ASR_SUPPORT
+            // If the test within the sched_lock indicating that the ASR
+            // be called was true, call it here. Calling the ASR must be
+            // the very last thing we do here, since it must run as close
+            // to "user" state as possible.
+        
+            if( call_asr ) current->asr(current->asr_data);
+#endif
 
-        CYG_POSTCONDITION( sched_lock == 0, "sched_lock not zero" );
+        }
+        else
+        {
+            // If new_lock is non-zero then we restore the sched_lock to
+            // the value given.
+            
+            HAL_REORDER_BARRIER();
+            
+            sched_lock = new_lock;
+            
+            HAL_REORDER_BARRIER();            
+        }
         
 #ifdef CYGDBG_KERNEL_TRACE_UNLOCK_INNER
         CYG_REPORT_RETURN();
@@ -270,6 +332,21 @@ cyg_bool Cyg_Scheduler::check_this( cyg_
 // SchedThread members
 
 // -------------------------------------------------------------------------
+// Static data members
+
+#ifdef CYGSEM_KERNEL_SCHED_ASR_SUPPORT
+
+# ifdef CYGSEM_KERNEL_SCHED_ASR_GLOBAL
+Cyg_ASR *Cyg_SchedThread::asr = &Cyg_SchedThread::asr_default;
+# endif
+
+# ifdef CYGSEM_KERNEL_SCHED_ASR_DATA_GLOBAL
+CYG_ADDRWORD Cyg_SchedThread::asr_data = 0;
+# endif
+
+#endif // CYGSEM_KERNEL_SCHED_ASR_SUPPORT
+
+// -------------------------------------------------------------------------
 // Constructor
 
 Cyg_SchedThread::Cyg_SchedThread(Cyg_Thread *thread, CYG_ADDRWORD sched_info)
@@ -282,42 +359,116 @@ Cyg_SchedThread::Cyg_SchedThread(Cyg_Thr
     if( Cyg_Scheduler::current_thread == NULL )
         Cyg_Scheduler::current_thread = thread;
 
-#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE_SIMPLE
+#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL
 
     mutex_count = 0;
+    
+#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_SIMPLE
+    
     priority_inherited = false;
     
 #endif
+#endif
+
+#ifdef CYGSEM_KERNEL_SCHED_ASR_SUPPORT
+
+    asr_inhibit = false;
+    asr_pending = false;
+
+#ifndef CYGSEM_KERNEL_SCHED_ASR_GLOBAL
+    asr = asr_default;
+#endif
+#ifdef CYGSEM_KERNEL_SCHED_ASR_DATA_GLOBAL
+    asr_data = NULL
+#endif        
     
+#endif    
 }
 
 // -------------------------------------------------------------------------
-// Priority inheritance support.
+// ASR support functions
+
+#ifdef CYGSEM_KERNEL_SCHED_ASR_SUPPORT
+
+// -------------------------------------------------------------------------
+// Set ASR
+// Install a new ASR, returning the old one.
+
+void Cyg_SchedThread::set_asr( Cyg_ASR  *new_asr, CYG_ADDRWORD  new_data,
+                  Cyg_ASR **old_asr, CYG_ADDRWORD *old_data)
+{
+    CYG_REPORT_FUNCTION();
 
-#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE
+    // Do this with the scheduler locked...
+    Cyg_Scheduler::lock();
+
+    if( old_asr != NULL ) *old_asr = asr;
+    if( old_data != NULL ) *old_data = asr_data;
+
+    // If new_asr is NULL, do not change the ASR,
+    // but only change the data.
+    if( new_asr != NULL ) asr = new_asr;
+    asr_data = new_data;
+    
+    Cyg_Scheduler::unlock();
+}
 
 // -------------------------------------------------------------------------
-// Inherit the priority of the provided thread if it
-// has a higher priority than ours.
+// Clear ASR
+
+void Cyg_SchedThread::clear_asr()
+{
+    CYG_REPORT_FUNCTION();
+
+    // Do this with the scheduler locked...
+    Cyg_Scheduler::lock();
 
-void Cyg_SchedThread::inherit_priority( Cyg_Thread *thread)
+    // Reset ASR to default.
+    asr = asr_default;
+    asr_data = 0;
+    
+    Cyg_Scheduler::unlock();    
+}
+
+// -------------------------------------------------------------------------
+// Default ASR function.
+// having this avoids our having to worry about ever seeing a NULL
+// pointer as the ASR function.
+
+void Cyg_SchedThread::asr_default(CYG_ADDRWORD data)
 {
-#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE_SIMPLE
+    CYG_REPORT_FUNCTION();
+
+    data=data;
+    return;
+}
+
+#endif
+
+// -------------------------------------------------------------------------
+// Generic priority protocol support
 
-    // 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.
+#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL
+
+void Cyg_SchedThread::set_inherited_priority( cyg_priority pri, Cyg_Thread *thread )
+{
+    CYG_REPORT_FUNCTION();
 
+#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_SIMPLE
+
+    // This is the comon code for priority inheritance and ceiling
+    // protocols. This implementation provides a simplified version of
+    // the protocol.
+    
     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!");
     
     // Compare with *current* priority in case thread has already
     // inherited - for relay case below.
-    if( thread->get_current_priority() < priority )
+    if( pri < priority )
     {
         cyg_priority mypri = priority;
         cyg_bool already_inherited = priority_inherited;
@@ -329,7 +480,7 @@ void Cyg_SchedThread::inherit_priority( 
 
         priority_inherited = false;     // so that set_prio DTRT
 
-        self->set_priority( thread->get_current_priority() );            
+        self->set_priority( pri );            
 
         if( !already_inherited )
             original_priority = mypri;
@@ -341,19 +492,15 @@ void Cyg_SchedThread::inherit_priority( 
 #endif
 }
 
-// -------------------------------------------------------------------------
-// Inherit the priority of the ex-owner thread or from the queue if it
-// has a higher priority than ours.
+void Cyg_SchedThread::relay_inherited_priority( Cyg_Thread *ex_owner, Cyg_ThreadQueue *pqueue)
+{
+    CYG_REPORT_FUNCTION();
 
-void Cyg_SchedThread::relay_priority( Cyg_Thread *ex_owner, Cyg_ThreadQueue *pqueue)
-{
-#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE_SIMPLE
+#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_SIMPLE
 
     // A simple implementation of priority inheritance.
     // At its simplest, this member does nothing.
 
-#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE_SIMPLE_RELAY
-
     // If there is anyone else waiting, then the *new* owner inherits from
     // the current one, since that is a maxima of the others waiting.
     // (It's worth not doing if there's nobody waiting to prevent
@@ -361,37 +508,29 @@ void Cyg_SchedThread::relay_priority( Cy
     // priority ceiling.
 
     if ( !pqueue->empty() )
-        inherit_priority( ex_owner );
+        set_inherited_priority( ex_owner->get_current_priority(), ex_owner );
 
 #endif
-#endif
 }
 
-// -------------------------------------------------------------------------
-// Lose a priority inheritance
+void Cyg_SchedThread::clear_inherited_priority()
+{
+    CYG_REPORT_FUNCTION();
 
-void Cyg_SchedThread::disinherit_priority()
-{
-#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE_SIMPLE
+#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_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.
+    // A simple implementation of priority inheritance/ceiling
+    // protocols.  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.
     
-    // The simplest algorithm also does not cause a 2nd owner (who waited)
-    // of a mutex to inherit from 3rd, 4th &c threads that are queueing up
-    // when it is awoken.  That limitation is avoided when
-    // CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE_SIMPLE_RELAY is also
-    // enabled, see above, which passes the raised priority from one thread
-    // to the next along with the mutex, like a relay baton.
-
     Cyg_Thread *self = CYG_CLASSFROMBASE(Cyg_Thread,
                                          Cyg_SchedThread,
                                          this);
@@ -408,10 +547,85 @@ void Cyg_SchedThread::disinherit_priorit
         
     }
     
-#endif    
+#endif        
+}
+
+#endif // CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL
+
+// -------------------------------------------------------------------------
+// Priority inheritance support.
+
+#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT
+
+// -------------------------------------------------------------------------
+// Inherit the priority of the provided thread if it
+// has a higher priority than ours.
+
+void Cyg_SchedThread::inherit_priority( Cyg_Thread *thread)
+{
+    CYG_REPORT_FUNCTION();
+
+    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!");
+
+    self->set_inherited_priority( thread->get_current_priority(), thread );
+    
+}
+
+// -------------------------------------------------------------------------
+// Inherit the priority of the ex-owner thread or from the queue if it
+// has a higher priority than ours.
+
+void Cyg_SchedThread::relay_priority( Cyg_Thread *ex_owner, Cyg_ThreadQueue *pqueue)
+{
+    CYG_REPORT_FUNCTION();
+
+    relay_inherited_priority( ex_owner, pqueue );
 }
 
-#endif // CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE of any kind
+// -------------------------------------------------------------------------
+// Lose a priority inheritance
+
+void Cyg_SchedThread::disinherit_priority()
+{
+    CYG_REPORT_FUNCTION();
+
+    CYG_ASSERT( mutex_count >= 0, "Non-positive mutex count");
+
+    clear_inherited_priority();
+}
+
+#endif // CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT
+
+// -------------------------------------------------------------------------
+// Priority ceiling support
+
+#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING
+
+void Cyg_SchedThread::set_priority_ceiling( cyg_priority pri )
+{
+    CYG_REPORT_FUNCTION();
+
+    CYG_ASSERT( mutex_count > 0, "Non-positive mutex count");
+
+    set_inherited_priority( pri );
+
+}
+
+void Cyg_SchedThread::clear_priority_ceiling( )
+{
+    CYG_REPORT_FUNCTION();
+
+    CYG_ASSERT( mutex_count >= 0, "Non-positive mutex count");
+
+    clear_inherited_priority();
+}
+
+#endif // CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING
 
 // -------------------------------------------------------------------------
 // EOF sched/sched.cxx