diff packages/kernel/current/tests/stress_threads.c @ 4:1d7f19c9e4d1 ecos-sw-1999-05-11

Merge from eCos master repository on 1999-05-11-21:11:10-BST
author jlarmour
date Tue, 11 May 1999 14:07:25 +0000
parents 443894e2e912
children ece80412419a
line wrap: on
line diff
--- a/packages/kernel/current/tests/stress_threads.c
+++ b/packages/kernel/current/tests/stress_threads.c
@@ -30,10 +30,19 @@
 //#####DESCRIPTIONBEGIN####
 //
 // Author(s):     rosalia
-// Contributors:  rosalia
+// Contributors:  rosalia, jskov
 // Date:          1999-04-13
 // Description:   Very simple thread stress test, with some memory
 //                allocation and alarm handling.
+//
+// Notes:
+//  If client_makes_request is big, it means that there are made many more
+//  client requests than can be serviced. Consequently, clients are wasting
+//  CPU time and should be sleeping more.
+//
+//  The list of handler invocations show how many threads are running
+//  at the same time. The more powerful the CPU, the more the numbers
+//  should spread out.
 //####DESCRIPTIONEND####
 
 #include <pkgconf/system.h>
@@ -65,50 +74,59 @@
 
 /* if TIME_LIMIT is defined, it represents the number of seconds this
    test should last; if it is undefined the test will go forever */
-#define DEATH_TIME_LIMIT 15
+#define DEATH_TIME_LIMIT 20
 /*  #undef DEATH_TIME_LIMIT */
 
 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL)
 #define STACK_SIZE2 (8*1024 + CYGNUM_HAL_STACK_SIZE_TYPICAL)
 
-#define N_CLIENTS 4
+/* Allocate priorities in this order. This ensures that handlers
+   (which are the ones using the CPU) get enough CPU time to actually
+   complete their tasks. */
+#define N_MAIN 1
+#define MAX_HANDLERS 19
 #define N_LISTENERS 4
-#define MAX_HANDLERS 19
+#define N_CLIENTS 4
 
-#if (CYGNUM_KERNEL_SCHED_PRIORITIES < (N_CLIENTS+N_LISTENERS+MAX_HANDLERS))
-# error "not enough priorities available"
-#endif
+#if (CYGNUM_KERNEL_SCHED_PRIORITIES >= (N_MAIN+MAX_HANDLERS+N_LISTENERS+N_CLIENTS))
 
 /* if we use the bitmap scheduler we must make sure we don't use the
    same priority more than once, so we must store those already in use */
-static char priority_in_use[N_CLIENTS+N_LISTENERS+MAX_HANDLERS];
+static volatile char priority_in_use[N_MAIN+MAX_HANDLERS+N_LISTENERS+N_CLIENTS];
 
 /* now declare (and allocate space for) some kernel objects, like the
    threads we will use */
-cyg_thread client_thread_s[N_CLIENTS];
+cyg_thread main_thread_s;
+cyg_thread handler_thread_s[MAX_HANDLERS];
 cyg_thread listener_thread_s[N_LISTENERS];
-cyg_thread handler_thread_s[MAX_HANDLERS];
+cyg_thread client_thread_s[N_CLIENTS];
 
 /* space for stacks for all threads */
-char client_stack[N_CLIENTS][STACK_SIZE];
+char main_stack[STACK_SIZE];
+char handler_stack[MAX_HANDLERS][STACK_SIZE2];
 char listener_stack[N_LISTENERS][STACK_SIZE];
-char handler_stack[MAX_HANDLERS][STACK_SIZE2];
+char client_stack[N_CLIENTS][STACK_SIZE];
 
 /* now the handles for the threads */
-cyg_handle_t clientH[N_CLIENTS];
-cyg_handle_t listenerH[N_LISTENERS];
+cyg_handle_t mainH;
 cyg_handle_t handlerH[MAX_HANDLERS];
-
-#ifdef DEATH_TIME_LIMIT
-/* how many client threads have been killed by the death handler */
-int n_clients_killed = 0;
-#endif /* DEATH_TIME_LIMIT */
+cyg_handle_t listenerH[N_LISTENERS];
+cyg_handle_t clientH[N_CLIENTS];
 
 /* and now variables for the procedure which is the thread */
-cyg_thread_entry_t client_program, listener_program, handler_program;
+cyg_thread_entry_t main_program, client_program, listener_program, 
+    handler_program;
 
 /* a few mutexes used in the code */
-cyg_mutex_t client_request_lock, handler_slot_lock, statistics_print_lock;
+cyg_mutex_t client_request_lock, handler_slot_lock, statistics_print_lock, 
+    free_handler_lock;
+
+/* global variables with which the handler IDs and thread priorities
+   to free are communicated from handlers to main_program. Access to
+   these are protected by free_handler_lock. An id of -1 means the
+   that the variables are empty. */
+volatile int free_handler_pri = 0;
+volatile int free_handler_id = -1;
 
 /* a global variable with which the client and server coordinate */
 int client_makes_request = 0;
@@ -138,23 +156,21 @@ struct s_statistics statistics;
 /* some function prototypes; those with the sc_ prefix are
    "statistics-collecting" versions of the cyg_ primitives */
 void sc_thread_create(
-    cyg_addrword_t        sched_info,           /* scheduling info (eg pri)  */
+    cyg_addrword_t      sched_info,             /* scheduling info (eg pri)  */
     cyg_thread_entry_t  *entry,                 /* entry point function      */
-    cyg_addrword_t        entry_data,           /* entry data                */
+    cyg_addrword_t      entry_data,             /* entry data                */
     char                *name,                  /* optional thread name      */
     void                *stack_base,            /* stack base, NULL = alloc  */
     cyg_ucount32        stack_size,             /* stack size, 0 = default   */
     cyg_handle_t        *handle,                /* returned thread handle    */
     cyg_thread          *thread                 /* put thread here           */
 );
-void sc_thread_exit(void);
 
 int get_handler_slot(cyg_handle_t current_threadH);
 void perform_stressful_tasks(void);
 void permute_array(char a[], int size, int seed);
 void setup_death_alarm(cyg_addrword_t data, cyg_handle_t *deathHp,
                        cyg_alarm *death_alarm_p, int *killed_p);
-void handle_death(cyg_handle_t deathH, cyg_handle_t alarmH);
 void print_statistics(void);
 
 /* we need to declare the alarm handling function (which is defined
@@ -175,35 +191,53 @@ void cyg_user_start(void)
 
   cyg_mutex_init(&client_request_lock);
   cyg_mutex_init(&statistics_print_lock);
+  cyg_mutex_init(&free_handler_lock);
 
   /* initialize statistics */
   memset(&statistics, 0, sizeof(statistics));
 
+  /* clear priority table */
+  for (i = 0; i < sizeof(priority_in_use); i++)
+      priority_in_use[i] = 0;
+
+  /* initialize main thread */
+  {
+      char thread_name[] = "main";
+
+      sc_thread_create(0, main_program, (cyg_addrword_t) 0,
+                       thread_name, (void *) main_stack, STACK_SIZE,
+                       &mainH, &main_thread_s);
+      priority_in_use[0]++;
+  }
+
   /* initialize all handler threads to not be in use */
   for (i = 0; i < MAX_HANDLERS; ++i) {
     handler_thread_in_use[i] = 0;
   }
+  for (i = 0; i < N_LISTENERS; ++i) {
+    int prio;
+    char thread_name[20];
+    sprintf(thread_name, "listener-%02d", i);
+    prio = N_MAIN + MAX_HANDLERS + i;
+    sc_thread_create(prio, listener_program, (cyg_addrword_t) i,
+                      thread_name, (void *) listener_stack[i], STACK_SIZE,
+                      &listenerH[i], &listener_thread_s[i]);
+    CYG_ASSERT(0 == priority_in_use[prio], "Priority already in use!");
+    priority_in_use[prio]++;
+  }
   for (i = 0; i < N_CLIENTS; ++i) {
     int prio;
     char thread_name[20];
     sprintf(thread_name, "client-%02d", i);
-    prio = i;
+    prio = N_MAIN + MAX_HANDLERS + N_LISTENERS + i;
     sc_thread_create(prio, client_program, (cyg_addrword_t) i,
                       thread_name, (void *) client_stack[i], STACK_SIZE,
                       &(clientH[i]), &client_thread_s[i]);
-    priority_in_use[prio] = 1;
-  }
-  for (i = 0; i < N_LISTENERS; ++i) {
-    int prio;
-    char thread_name[20];
-    sprintf(thread_name, "listener-%02d", i);
-    prio = N_CLIENTS + i;
-    sc_thread_create(prio, listener_program, (cyg_addrword_t) i,
-                      thread_name, (void *) listener_stack[i], STACK_SIZE,
-                      &listenerH[i], &listener_thread_s[i]);
-    priority_in_use[prio] = 1;
+    CYG_ASSERT(0 == priority_in_use[prio], "Priority already in use!");
+    priority_in_use[prio]++;
   }
 
+  cyg_thread_resume(mainH);
   for (i = 0; i < N_CLIENTS; ++i) {
     cyg_thread_resume(clientH[i]);
   }
@@ -220,11 +254,75 @@ void cyg_user_start(void)
                    (cyg_addrword_t) 4000,
                    &report_alarmH, &report_alarm);
   if (cyg_test_is_simulator) {
-    cyg_alarm_initialize(report_alarmH, cyg_current_time()+300, 400);
+    cyg_alarm_initialize(report_alarmH, cyg_current_time()+200, 200);
   } else {
     cyg_alarm_initialize(report_alarmH, cyg_current_time()+300, 4000);
   }
+}
 
+/* main_program() -- frees resources and prints status. */
+void main_program(cyg_addrword_t data)
+{
+#ifdef DEATH_TIME_LIMIT
+    cyg_handle_t deathH;
+    cyg_alarm death_alarm;
+    int is_dead = 0;
+
+    setup_death_alarm(0, &deathH, &death_alarm, &is_dead);
+#endif /* DEATH_TIME_LIMIT */
+
+    printf("# Starting main\n");
+
+    for (;;) {
+        int handler_id = -1;
+        int handler_pri = 0;
+
+        cyg_mutex_lock(&free_handler_lock); {
+            // If any handler has left its ID, copy the ID and
+            // priority values to local variables, and free up the
+            // global communication variables again.
+            if (-1 != free_handler_id) {
+                handler_id = free_handler_id;
+                handler_pri = free_handler_pri;
+                free_handler_id = -1;
+            }
+        } cyg_mutex_unlock(&free_handler_lock);
+
+        if (-1 != handler_id) {
+            // Free the handler resources. This is done outside of the
+            // free_handler_lock to avoid deadlocks.
+            cyg_mutex_lock(&handler_slot_lock); {
+                CYG_ASSERT(1 == priority_in_use[handler_pri], 
+                           "Priority not in use!");
+                CYG_ASSERT(1 == handler_thread_in_use[handler_id], 
+                           "Handler not in use!");
+                handler_thread_in_use[handler_id]--;
+                priority_in_use[handler_pri]--;
+                // Finally delete the handler thread.
+                {
+                    // workaround for PRs 20054-20058/20065
+                    cyg_thread_kill(handlerH[handler_id]);
+                }
+                cyg_thread_delete(handlerH[handler_id]);
+            } cyg_mutex_unlock(&handler_slot_lock);
+        }
+
+        // Print status if time.
+        if (time_to_report) {
+            time_to_report = 0;
+            print_statistics();
+        }
+
+#ifdef DEATH_TIME_LIMIT
+        // Stop test if time.
+        if (is_dead) {
+            print_statistics();
+            CYG_TEST_PASS_FINISH("Kernel thread stress test OK");
+        }
+#endif /* DEATH_TIME_LIMIT */
+
+        cyg_thread_delay(3);
+    }
 }
 
 /* client_program() -- an obnoxious client which makes a lot of requests */
@@ -232,19 +330,13 @@ void client_program(cyg_addrword_t data)
 {
   int delay;
 
-  cyg_handle_t counterH, deathH, system_clockH;
-  cyg_alarm death_alarm;
-  int is_dead = 0;
-
-  setup_death_alarm(data, &deathH, &death_alarm, &is_dead);
-
   printf("# Starting client-%d\n", (int) data);
 
   system_clockH = cyg_real_time_clock();
   cyg_clock_to_counter(system_clockH, &counterH);
 
   for (;;) {
-    delay = (rand() % 3);
+    delay = (rand() % 20);
 
     /* now send a request to the server */
     cyg_mutex_lock(&client_request_lock); {
@@ -254,11 +346,6 @@ void client_program(cyg_addrword_t data)
 
     cyg_thread_delay(10+delay);
 /*      cyg_thread_delay(0); */
-#ifdef DEATH_TIME_LIMIT
-    if (is_dead) {
-      handle_death(deathH, report_alarmH);
-    }
-#endif /* DEATH_TIME_LIMIT */
   }
 }
 
@@ -267,40 +354,40 @@ void client_program(cyg_addrword_t data)
 void listener_program(cyg_addrword_t data)
 {
 /*   int message = (int) data; */
-  int handler_slot;
+    int handler_slot;
 
-  printf("# Beginning execution; thread data is %d\n", (int) data);
+    printf("# Beginning execution; thread data is %d\n", (int) data);
 
-  for (;;) {
-#ifdef DEATH_TIME_LIMIT
-    /* as an extra task, the listener sees if all clients have been
-       killed off, so it can report that the test is over */
-    if (n_clients_killed == N_CLIENTS) {
-      n_clients_killed = -1;    /* so we don't call this again */
-      CYG_TEST_PASS_FINISH("Kernel thread stress test OK");
+    for (;;) {
+        int make_request = 0;
+        cyg_mutex_lock(&client_request_lock); {
+            if (client_makes_request > 0) {
+                --client_makes_request;
+                make_request = 1;
+            }
+        } cyg_mutex_unlock(&client_request_lock);
+        
+        if (make_request) {
+            int prio;
+            /* printf("just got a request from a client (count = %d)\n", */
+            /*        client_makes_request); */
+
+            handler_slot = get_handler_slot(listenerH[(int) data]);
+            prio = N_MAIN+handler_slot;
+
+            CYG_ASSERT(0 == priority_in_use[prio], "Priority already in use!");
+            priority_in_use[prio]++;
+            sc_thread_create(prio, handler_program,
+                             (cyg_addrword_t) handler_slot,
+                             "handler", (void *) handler_stack[handler_slot],
+                             STACK_SIZE2, &handlerH[handler_slot],
+                             &handler_thread_s[handler_slot]);
+            cyg_thread_resume(handlerH[handler_slot]);
+            ++statistics.handler_invocation_histogram[handler_slot];
+        }
+
+        cyg_thread_delay(2 + (rand() % 10));
     }
-#endif /* DEATH_TIME_LIMIT */
-    if (client_makes_request > 0) {
-      int prio;
-      /*          printf("just got a request from a client (count = %d)\n", */
-      /*                 client_makes_request); */
-      cyg_mutex_lock(&client_request_lock); {
-        --client_makes_request;
-      } cyg_mutex_unlock(&client_request_lock);
-
-      handler_slot = get_handler_slot(listenerH[(int) data]);
-      prio = N_CLIENTS+N_LISTENERS+handler_slot;
-      priority_in_use[prio] = 1;
-      sc_thread_create(prio, handler_program,
-                       (cyg_addrword_t) handler_slot,
-                       "handler", (void *) handler_stack[handler_slot],
-                       STACK_SIZE2, &handlerH[handler_slot],
-                       &handler_thread_s[handler_slot]);
-      cyg_thread_resume(handlerH[handler_slot]);
-      ++statistics.handler_invocation_histogram[handler_slot];
-    }
-    cyg_thread_delay(1);
-  }
 }
 
 /* handler_program() -- is spawned to handle each incoming request */
@@ -309,56 +396,56 @@ void handler_program(cyg_addrword_t data
   /* here is where we perform specific stressful tasks */
   perform_stressful_tasks();
 
-  if (time_to_report) {
-    time_to_report = 0;
-    print_statistics();
-  }
-
   cyg_thread_delay(4 + (int) (0.5*log(1.0 + fabs((rand() % 1000000)))));
 /*    cyg_thread_delay(0); */
 
-  /* lock the scheduler before we declare this thread slot available
-     and quit; note that cyg_thread_exit() will unlock the scheduler
-     as many times as necessary */
-  cyg_mutex_lock(&handler_slot_lock); {
-    handler_thread_in_use[data] = 0;
-    priority_in_use[N_CLIENTS + N_LISTENERS + (int) data] = 0;
-  } cyg_mutex_unlock(&handler_slot_lock);
-  /* FIXME: could there be a race condition right here? I unlock the
-     scheduler, so I could get pre-empted out, but meanwhile I have
-     declared this thread available again.  must fix it. */
-  sc_thread_exit();
+  ++statistics.thread_exits;
+  {
+      // Loop until the handler id and priority can be communicated to
+      // the main_program.
+      int freed = 0;
+      do {
+          cyg_mutex_lock(&free_handler_lock); {
+              if (-1 == free_handler_id) {
+                  free_handler_id = data;
+                  free_handler_pri = N_MAIN+(int) data;
+                  freed = 1;
+              }
+          } cyg_mutex_unlock(&free_handler_lock);
+          if (!freed)
+              cyg_thread_delay(2);
+      } while (!freed);
+  }
+
+  // Then wait for the main_program to kill us.
+  for (;;) {
+      cyg_thread_delay(100);
+  }
 }
 
 /* look for an available handler thread */
 int get_handler_slot(cyg_handle_t current_threadH)
 {
-  int i;
-  int found = 0;
+    int i;
+    int found = 0;
 
-  while (!found) {
-    for (i = 0; i < MAX_HANDLERS; ++i) {
-      cyg_mutex_lock(&handler_slot_lock); {
-        if (!handler_thread_in_use[i]) {
-          found = 1;
-          handler_thread_in_use[i] = 1;
-        }
-      } cyg_mutex_unlock(&handler_slot_lock);
-      if (found) {
-        break;
-      }
-#ifdef DEATH_TIME_LIMIT
-      /* must do a check here to see if all clients have been killed,
-         since otherwise we might end up in an infinite loop */
-      if (n_clients_killed == N_CLIENTS) {
-	n_clients_killed = -1;    /* so we don't call this again */
-	CYG_TEST_PASS_FINISH("Kernel thread stress test OK");
-      }
-#endif
+    while (!found) {
+        cyg_mutex_lock(&handler_slot_lock); {
+            for (i = 0; i < MAX_HANDLERS; ++i) {
+                if (!handler_thread_in_use[i]) {
+                    found = 1;
+                    handler_thread_in_use[i]++;
+                    break;
+                }
+            }
+        } cyg_mutex_unlock(&handler_slot_lock);
+        if (!found)
+            cyg_thread_delay(1);
     }
-    cyg_thread_delay(1);
-  }
-  return i;
+
+    CYG_ASSERT(1 == handler_thread_in_use[i], "Handler usage count wrong!");
+
+    return i;
 }
 
 /* do things which will stress the system */
@@ -386,9 +473,12 @@ void perform_stressful_tasks()
     ++statistics.malloc_tries;
 /*      spaces[i] = (char *) malloc(((int)(sqrt(i*2.0))+1)*MALLOCED_BASE_SIZE); */
     spaces[i] = (char *) malloc(((int)i*2.0+1)*MALLOCED_BASE_SIZE);
-    if (i % 100 == 0) {
+    if (i % (MAX_MALLOCED_SPACES/10) == 0) {
       cyg_thread_yield();
     }
+    if (i % (MAX_MALLOCED_SPACES/15) == 0) {
+      cyg_thread_delay(i % 5);
+    }
   }
 
   /* now free it all up */
@@ -426,7 +516,7 @@ void perform_stressful_tasks()
 
 /* report_alarm_func() is invoked as an alarm handler, so it should be
    quick and simple.  in this case it sets a global flag which is
-   checked by threads. */
+   checked by main_program. */
 void report_alarm_func(cyg_handle_t alarmH, cyg_addrword_t data)
 {
     time_to_report = 1;
@@ -437,7 +527,6 @@ void report_alarm_func(cyg_handle_t alar
 void setup_death_alarm(cyg_addrword_t data, cyg_handle_t *deathHp,
                        cyg_alarm *death_alarm_p, int *killed_p)
 {
-#ifdef DEATH_TIME_LIMIT
   cyg_handle_t system_clockH, counterH;
   cyg_resolution_t rtc_res;
 
@@ -455,9 +544,14 @@ void setup_death_alarm(cyg_addrword_t da
        *((double)DEATH_TIME_LIMIT)/((double)rtc_res.dividend));
     if ( cyg_test_is_simulator )
         tick_delay /= 10;
+#ifdef CYGPKG_HAL_I386_LINUX 
+    // 20 seconds is a long time compared to the run time of other tests.
+    // Reduce to 10 seconds, allowing more tests to get run.
+    tick_delay /= 2;
+#endif
+
     cyg_alarm_initialize(*deathHp, cyg_current_time() + tick_delay, 0);
   }
-#endif /* DEATH_TIME_LIMIT */
 }
 
 /* death_alarm_func() is the alarm handler that kills the current
@@ -470,23 +564,11 @@ void death_alarm_func(cyg_handle_t alarm
   *killed_p = 1;
 }
 
-#ifdef DEATH_TIME_LIMIT
-/* handle_death is called by a client thread when it dies; it kills
-   off the alarm */
-void handle_death(cyg_handle_t deathH, cyg_handle_t alarmH)
-{
-  ++n_clients_killed;
-  cyg_alarm_delete(deathH);
-  cyg_alarm_delete(alarmH);
-  cyg_thread_exit();
-}
-#endif /* DEATH_TIME_LIMIT */
-
 /* now I write the sc_ versions of the cyg_functions */
 void sc_thread_create(
-    cyg_addrword_t        sched_info,          /* scheduling info (eg pri)  */
+    cyg_addrword_t      sched_info,            /* scheduling info (eg pri)  */
     cyg_thread_entry_t  *entry,                /* entry point function      */
-    cyg_addrword_t        entry_data,          /* entry data                */
+    cyg_addrword_t      entry_data,            /* entry data                */
     char                *name,                 /* optional thread name      */
     void                *stack_base,           /* stack base, NULL = alloc  */
     cyg_ucount32        stack_size,            /* stack size, 0 = default   */
@@ -501,14 +583,6 @@ void sc_thread_create(
                     stack_base, stack_size, handle, thread);
 }
 
-void sc_thread_exit()
-{
-/*    printf("exiting\n"); */
-/*    fflush(stdout); */
-  ++statistics.thread_exits;
-  cyg_thread_exit();
-}
-
 void print_statistics(void)
 {
   int i;
@@ -525,6 +599,12 @@ void print_statistics(void)
   } cyg_mutex_unlock(&statistics_print_lock);
 }
 
+#else /* (CYGNUM_KERNEL_SCHED_PRIORITIES >=    */
+      /* (N_MAIN+N_CLIENTS+N_LISTENERS+MAX_HANDLERS)) */
+#define N_A_MSG "not enough priorities available"
+#endif /* (CYGNUM_KERNEL_SCHED_PRIORITIES >=    */
+       /* (N_MAIN+N_CLIENTS+N_LISTENERS+MAX_HANDLERS)) */
+
 #else /* CYGSEM_LIBC_MALLOC */
 # define N_A_MSG "this test needs malloc"
 #endif /* CYGSEM_LIBC_MALLOC */