t_safe_list.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : test_safe_list                                                    *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 1997-$now By Author.  This program is free software; you can  *
00008 * redistribute it and/or modify it under the terms of the GNU General Public  *
00009 * License as published by the Free Software Foundation; either version 2 of   *
00010 * the License or (at your option) any later version.  This is online at:      *
00011 *     http://www.fsf.org/copyleft/gpl.html                                    *
00012 * Please send any updates to: fred@gruntose.com                               *
00013 \*****************************************************************************/
00014 
00015 #include <basis/chaos.h>
00016 #include <basis/guards.h>
00017 #include <basis/istring.h>
00018 #include <data_struct/amorph.cpp>
00019 #include <mechanisms/ithread.h>
00020 #include <mechanisms/safe_roller.h>
00021 #include <mechanisms/time_stamp.h>
00022 #include <nodes/node.h>
00023 #include <nodes/safe_list.h>
00024 #include <nodes/safe_node.h>
00025 #include <loggers/file_logger.h>
00026 #include <opsystem/path_configuration.h>
00027 #include <data_struct/static_memory_gremlin.h>
00028 
00029 #ifdef __WIN32__
00030   #include <process.h>
00031 #endif
00032 
00033 HOOPLE_STARTUP_CODE;
00034 
00035 using namespace nodes;
00036 
00037 #define DEBUG_SAFE_LIST
00038   // uncomment this line to get more debugging output (into a file called
00039   // LOGFILE_NAME).
00040 
00041 const int DEFAULT_LIFE_TIME = 4 * MINUTE_ms;
00042   // the amount of time the program runs by default.
00043 
00044 chaos rando;
00045 
00046 // the type we're storing in the list.
00047 
00048 class t_node : public safe_node
00049 {
00050 public:
00051   int _int;
00052 
00053   t_node(int a = 0) : _int(a) {}
00054   operator int() const { return _int; }
00055   operator int() { return _int; }
00056   t_node &operator =(const t_node &to_copy)
00057       { _int = to_copy._int; return *this; }
00058   t_node &operator =(int to_copy) { _int = to_copy; return *this; }
00059 };
00060 
00061 #define CASTER(bare_node) static_cast<const t_node *>(bare_node)
00062   // takes a node pointer and turns it back into a pointer to a t_node.
00063 
00064 static int thread_count = 0;
00065   // a variable that will be used to track how many threads are active.
00066 
00067 const istring LOGFILE_NAME = path_configuration::make_logfile_name
00068     ("t_safe_list.log");
00069   // where our debugging output goes by default.
00070 
00071 #define LOG(to_print) log.log(timestamp(true) + istring(to_print))
00072   // our macro for logging with a timestamp.
00073 
00074 const int NUMBER_OF_CHECKERS = 8;
00075   // the number of threads checking the list simultaneously.
00076 
00077 class adder_thread : public ithread
00078 {
00079 public:
00080   safe_list &_the_list;
00081 
00082   adder_thread(safe_list &the_list) : ithread(), _the_list(the_list) {}
00083 
00084   void perform_activity(void *) {
00085     safe_add(thread_count, 1);
00086 //    safe_list *the_list = (safe_list *)hidden_pointer;
00087     file_logger log(LOGFILE_NAME);
00088 LOG(istring(istring::SPRINTF, "adder is thread %d.", thread_count));
00089 
00090     // seek the correct insertion place to keep the list ordered.
00091     safe_list_write_iterator *iter = _the_list.open_writer();
00092     // select a random number to add to the list.
00093     int to_add = rando.inclusive(0, 100000);
00094     LOG(istring(istring::SPRINTF, "entering adder (%d).", to_add));
00095     while (!iter->is_tail() && iter->access()
00096         && (CASTER(iter->access())->operator int() <= to_add) )
00097       iter->next();
00098     _the_list.insert(*iter, new t_node(to_add));
00099     LOG("exiting adder.");
00100     _the_list.close_writer(iter);
00101     safe_add(thread_count, -1);
00102   }
00103 };
00104 
00105 class deleter_thread : public ithread
00106 {
00107 public:
00108   safe_list &_the_list;
00109 
00110   deleter_thread(safe_list &the_list) : ithread(), _the_list(the_list) {}
00111 
00112   void perform_activity(void *) {
00113     safe_add(thread_count, 1);
00115     file_logger log(LOGFILE_NAME);
00116 LOG(istring(istring::SPRINTF, "deleter is thread %d.", thread_count));
00117 
00118     // start at the head of the list...
00119     safe_list_write_iterator *iter = _the_list.open_writer();
00120     LOG("entering deleter.");
00121     int elem = _the_list.elements(*iter);
00122     if (elem > 0) {
00123       int to_whack = rando.inclusive(0, elem - 1);
00124 LOG(istring(istring::SPRINTF, "deleter sees %d items and will whack #%d.", elem, to_whack));
00125       // jump to the element we chose.
00126       while (--to_whack > 0) iter->next();
00128       if (!iter->access())
00129         deadly_error("deleter", "movement code", "not at the right index");
00130       LOG(istring(istring::SPRINTF, "zap (%d).",
00131           CASTER(iter->access())->operator int()));
00132       bool zap_it = rando.inclusive(0, 1);
00133       if (zap_it) _the_list.zap(*iter);
00134       else {
00135         safe_node *to_whack = _the_list.remove(*iter);
00136         WHACK(to_whack);
00137       }
00138     }
00139     LOG("exiting deleter.");
00140     _the_list.close_writer(iter);
00141     safe_add(thread_count, -1);
00142   }
00143 };
00144 
00145 static bool exit_now = false;
00146   // true if the program is in a shutting down state.
00147 
00148 class checker_thread : public ithread
00149 {
00150 public:
00151   safe_list &_the_list;
00152 
00153   checker_thread(safe_list &the_list) : ithread(), _the_list(the_list) {}
00154 
00155   void perform_activity(void *) {
00156     safe_add(thread_count, 1);
00158     file_logger log(LOGFILE_NAME);
00159     LOG("entering checker.");
00160 LOG(istring(istring::SPRINTF, "checker is thread %d.", thread_count));
00161 
00162     while (!exit_now) {
00163       // test the list invariant (which is that all elements should be sorted
00164       // in non-decreasing order).
00165       safe_list_read_iterator *iter = _the_list.open_reader(safe_list::TAIL);
00166 LOG("thread got access with iter.");
00167       LOG("started checking list.");
00168       // we only bother to check the list if it's not empty.
00169       if (!_the_list.empty(*iter)) {
00170         // initialize our comparator.
00171         int bigger = CASTER(iter->observe())->operator int();
00172         // loop backwards until we hit the head.
00173         while (!iter->is_head()) {
00174           // check that the last value is not less than the current value.
00175           if (bigger < CASTER(iter->observe())->operator int())
00176             deadly_error("t_list", "invariant check",
00177                 "found a mal-ordering in the list");
00178           bigger = CASTER(iter->observe())->operator int();
00179           iter->previous();
00180         }
00181       }
00182       LOG("finished checking list.");
00183 LOG("thread giving up access with iter.");
00184       _the_list.close_reader(iter);
00185       int milliseconds_to_sleep = rando.inclusive(200, 828);
00186       time_stamp wake_up_time(milliseconds_to_sleep);
00187         // pause the thread until it's time to wake up (or leave).
00188       while (!exit_now && (time_stamp() < wake_up_time)) {
00189 LOG("checker zzzz...");
00190         portable::sleep_ms(72);
00191       }
00192     }
00193     LOG("exiting checker.");
00194     safe_add(thread_count, -1);
00195   }
00196 };
00197   
00198 int main(int formal(argc), char *formal(argv)[])
00199 {
00200   safe_list the_list;
00201   int time_to_run = DEFAULT_LIFE_TIME;
00202     // the time to run is how long the test will last.
00203 //hmmm: get time to run from command line.
00204 
00205   file_logger log(LOGFILE_NAME);
00206   log.log(""); log.log(""); log.log("");  // some blank lines.
00207   LOG("starting test.");
00208 
00209   amorph<ithread> thread_list;
00210 
00211   // start the threads that check list integrity.
00212   for (int i = 0; i < NUMBER_OF_CHECKERS; i++)
00213     thread_list.append(new checker_thread(the_list));
00214 
00215   bool sleep_time = false;
00216     // are we in a sleepy mode where we don't create new threads?
00217   time_stamp run_until(time_to_run);
00218     // this is the time when we should get out of here.
00219   while (run_until > time_stamp()) {
00220     // do some random things to the list until it's time to get out.
00221     if (sleep_time) {
00222 LOG("main loop zzzz...");
00223       portable::sleep_ms(84);
00224       // we have a chance to get out of sleep mode next time around.
00225       if (rando.inclusive(1, 100) > 95) {
00226         sleep_time = false;
00227         LOG("<waking up.>");
00228         exit_now = false;
00229       }
00230       continue;  // crank through loop again.
00231     }
00232     if (rando.inclusive(0, 100) == 58) {
00233       // we hit the magical sleeper period.
00234       sleep_time = true;
00235       LOG("<going to sleep.>");
00236       exit_now = true;  // tell all threads to exit during snoozy time.
00237       continue;
00238     }
00239     int selector = rando.inclusive(1, 100);
00240     // there's currently a 15% chance of adding an item, a 6% chance of
00241     // removing an item and a 21% chance of starting a checker..
00242     if ( (selector >= 14) && (selector <= 28) ) {
00243       LOG("launching an adder.");
00244       thread_list.append(new adder_thread(the_list));
00245     } else if ( (selector >= 69) && (selector <= 74) ) {
00246       LOG("launching a deleter.");
00247       thread_list.append(new deleter_thread(the_list));
00248     } else if ( (selector >= 29) && (selector <= 49) ) {
00249        LOG("launching a checker.");
00250       thread_list.append(new checker_thread(the_list));
00251     }
00252 //hmmm: add more types of threads we could call.
00253   }
00254 
00255   // wait until all the threads have exited.
00256   LOG("now exiting from all threads...");
00257   exit_now = true;
00258   while (thread_count > 0) { portable::sleep_ms(42); }
00259   LOG("done exiting from all threads.");
00260 
00261 #ifdef DEBUG_SAFE_LIST
00262   safe_list_read_iterator *iter = the_list.open_reader();
00263   LOG("");
00264   LOG("safe_list contents:");
00265   if (the_list.empty(*iter)) {
00266     LOG("the list is empty.");
00267   } else {
00268     int indy = 0;
00269     while (!iter->is_tail()) {
00270       int item = CASTER(iter->observe())->operator int();
00271       LOG(istring(istring::SPRINTF, "item #%d: %d", indy, item));
00272       indy++;
00273       iter->next();
00274     }
00275   }
00276   the_list.close_reader(iter);
00277 #endif
00278   LOG("exiting test");
00279 
00280   thread_list.reset();  // stop all the threads.
00281 
00282   guards::alert_message("safe_list:: works for those functions tested.");
00283   return 0;
00284 }
00285 

Generated on Fri Nov 21 04:30:13 2008 for HOOPLE Libraries by  doxygen 1.5.1