diff packages/net/tcpip/current/src/ecos/timeout.c @ 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 02ea4320376c
children 6bd9d475ed4b
line wrap: on
line diff
--- a/packages/net/tcpip/current/src/ecos/timeout.c
+++ b/packages/net/tcpip/current/src/ecos/timeout.c
@@ -41,8 +41,8 @@
 //==========================================================================
 //#####DESCRIPTIONBEGIN####
 //
-// Author(s):     gthomas
-// Contributors:  gthomas
+// Author(s):     gthomas, hmt
+// Contributors:  gthomas, hmt
 // Date:          1999-02-05
 // Description:   Simple timeout functions
 //####DESCRIPTIONEND####
@@ -50,6 +50,7 @@
 #include <sys/param.h>
 #include <pkgconf/net.h>
 #include <cyg/kernel/kapi.h>
+#include <cyg/infra/cyg_ass.h>
 
 // Timeout support
 
@@ -65,30 +66,55 @@ static timeout_entry timeouts[NTIMEOUTS]
 static cyg_handle_t timeout_alarm_handle;
 static cyg_alarm timeout_alarm;
 static cyg_int32 last_delta;
+static cyg_tick_count_t last_set_time;
+
+extern cyg_uint32 cyg_in_softnet( void );
 
 static void
 do_timeout(cyg_handle_t alarm, cyg_addrword_t data)
 {
     int i;
     cyg_int32 min_delta;
-    timeout_entry *e = timeouts;
-    min_delta = 0x7FFFFFFF;  // Maxint
-    for (i = 0;  i < NTIMEOUTS;  i++, e++) {
+    timeout_entry *e;
+
+    CYG_ASSERT( 0 < last_delta, "last_delta underflow" );
+
+    min_delta = last_delta; // local copy
+    last_delta = -1; // flag recursive call underway
+
+    for (e = timeouts, i = 0;  i < NTIMEOUTS;  i++, e++) {
         if (e->delta) {
-            e->delta -= last_delta;
-            if (e->delta == 0) {
+            CYG_ASSERT( e->delta >= min_delta, "e->delta underflow" );
+            e->delta -= min_delta;
+            if (e->delta <= 0) { // Defensive
                 // Time for this item to 'fire'
-                (e->fun)(e->arg);
+                timeout_fun *fun = e->fun;
+                void *arg = e->arg;
+                // Call it *after* cleansing the record
                 e->fun = 0;
-            } else {
-                if (e->delta < min_delta) min_delta = e->delta;
+                e->delta = 0;
+                (*fun)(arg);
             }
         }
     }
+
+    // Now scan for a new timeout *after* running all the callbacks
+    // (because they can add timeouts themselves)
+    min_delta = 0x7FFFFFFF;  // Maxint
+    for (e = timeouts, i = 0;  i < NTIMEOUTS;  i++, e++)
+        if (e->delta)
+            if (e->delta < min_delta)
+                min_delta = e->delta;
+
+    CYG_ASSERT( 0 < min_delta, "min_delta underflow" );
+
     if (min_delta != 0x7FFFFFFF) {
         // Still something to do, schedule it
-        cyg_alarm_initialize(timeout_alarm_handle, cyg_current_time()+min_delta, 0);
+        last_set_time = cyg_current_time();
+        cyg_alarm_initialize(timeout_alarm_handle, last_set_time+min_delta, 0);
         last_delta = min_delta;
+    } else {
+        last_delta = 0; // flag no activity
     }
 }
 
@@ -96,18 +122,31 @@ cyg_uint32
 timeout(timeout_fun *fun, void *arg, cyg_int32 delta)
 {
     int i;
-    cyg_int32 min_delta;
     static bool init = false;
-    timeout_entry *e = timeouts;
+    timeout_entry *e;
     cyg_uint32 stamp;
+
+    CYG_ASSERT( 0 < delta, "delta is right now, or even sooner!" );
+
+    // this needs to be atomic wrt threads and DSRs
+    cyg_scheduler_lock();
+
     if (!init) {
         cyg_handle_t h;
         cyg_clock_to_counter(cyg_real_time_clock(), &h);
         cyg_alarm_create(h, do_timeout, 0, &timeout_alarm_handle, &timeout_alarm);
         init = true;
     }
+
+    // Renormalize delta wrt the existing set alarm, if there is one
+    if ( last_delta > 0 )
+        delta += (cyg_int32)(cyg_current_time() - last_set_time);
+    // So recorded_delta is set to either:
+    // alarm is active:   delta + NOW - THEN
+    // alarm is inactive: delta
+
     stamp = 0;  // Assume no slots available
-    for (i = 0;  i < NTIMEOUTS;  i++, e++) {
+    for (e = timeouts, i = 0;  i < NTIMEOUTS;  i++, e++) {
         if ((e->delta == 0) && (e->fun == 0)) {
             // Free entry
             e->delta = delta;
@@ -117,31 +156,71 @@ timeout(timeout_fun *fun, void *arg, cyg
             break;
         }
     }
-    e = timeouts;
-    min_delta = 0x7FFFFFFF;
-    for (i = 0;  i < NTIMEOUTS;  i++, e++) {
-        if (e->delta && (e->delta < min_delta)) min_delta = e->delta;
+
+    if ( stamp &&                 // we did add a record AND
+         (0 == last_delta ||      // alarm was inactive  OR
+          delta < last_delta) ) { // alarm was active but later than we need
+
+        // (if last_delta is -1, this call is recursive from the handler so
+        //  also do nothing in that case)
+
+        // Here, we know the new item added is sooner than that which was
+        // most recently set, if any, so we can just go and set it up.
+        if ( 0 == last_delta )
+            last_set_time = cyg_current_time();
+        
+        // So we use, to set the alarm either:
+        // alarm is active:   (delta + NOW - THEN) + THEN
+        // alarm is inactive:  delta + NOW
+        // and in either case it is true that
+        //  (recorded_delta + last_set_time) == (delta + NOW)
+        cyg_alarm_initialize(timeout_alarm_handle, last_set_time+delta, 0);
+        last_delta = delta;
     }
-    if (min_delta != 0x7FFFFFFF) {
-        // Still something to do, schedule it
-        cyg_alarm_initialize(timeout_alarm_handle, cyg_current_time()+min_delta, 0);
-        last_delta = min_delta;
+    // Otherwise, the alarm is active, AND it is set to fire sooner than we
+    // require, so when it does, that will sort out calling the item we
+    // just added.  Or we didn't actually add a record, so nothing has
+    // changed.
+
+#ifdef CYGPKG_INFRA_DEBUG
+    // Do some more checking akin to that in the alarm handler:
+    if ( last_delta != -1 ) { // not a recursive call
+        CYG_ASSERT( last_delta >= 0, "Bad last delta" );
+        delta = 0x7fffffff;
+        for (e = timeouts, i = 0;  i < NTIMEOUTS;  i++, e++) {
+            if (e->delta) {
+                CYG_ASSERT( e->delta >= last_delta, "e->delta underflow" );
+                CYG_ASSERT( last_set_time + e->delta > cyg_current_time(),
+                            "Recorded alarm not in the future!" );
+                if ( e->delta < delta )
+                    delta = e->delta;
+            } else {
+                CYG_ASSERT( 0 == e->fun, "Function recorded for 0 delta" );
+            }
+        }
+        CYG_ASSERT( delta == last_delta, "We didn't pick the smallest delta!" );
     }
+#endif
+
+    cyg_scheduler_unlock();
+
     return stamp;
 }
 
 void
 untimeout(timeout_fun *fun, void * arg)
 {
-  int i;
-  timeout_entry *e = timeouts;
-
-  for (i = 0; i < NTIMEOUTS; i++, e++) {
-    if (e->delta && (e->fun == fun) && (e->arg == arg)) {
+    int i;
+    timeout_entry *e;
+    cyg_scheduler_lock();
+    for (e = timeouts, i = 0; i < NTIMEOUTS; i++, e++) {
+        if (e->delta && (e->fun == fun) && (e->arg == arg)) {
             e->delta = 0;
             e->fun = 0;
-      return;
+            break;
         }
     }
+    cyg_scheduler_unlock();
 }
 
+// EOF timeout.c