t_rw_lock.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : test_reader_writer_lock                                           *
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/array.cpp>
00016 #include <basis/chaos.h>
00017 #include <basis/guards.h>
00018 #include <basis/istring.h>
00019 #include <basis/mutex.h>
00020 #include <mechanisms/ithread.h>
00021 #include <mechanisms/safe_roller.h>
00022 #include <mechanisms/rw_lock.h>
00023 #include <mechanisms/time_stamp.h>
00024 #include <opsystem/application_shell.h>
00025 #include <opsystem/path_configuration.h>
00026 #include <data_struct/static_memory_gremlin.h>
00027 
00028 HOOPLE_STARTUP_CODE;
00029 
00030 //#define DEBUG_RW_LOCK
00031   // uncomment if you want a noisy debugging version.  careful; this can
00032   // change the interaction properties of the threads because it introduces
00033   // some delays and some file lockings where there would be none otherwise.
00034 
00035 typedef int protectee;
00036   // the type we're protecting with a reader / writer lock.
00037 
00038 const int DEFAULT_LIFE_TIME = 2 * MINUTE_ms;
00039   // the length of time the test runs.
00040 
00041 const int NUMBER_OF_CHECKERS = 28;
00042   // the guys who check the value.
00043 
00044 //hmmm: fix the logging to use the real debug macros.
00045 
00047 
00048 chaos rando;
00049 
00051 
00052 // this class could easily be part of the tester object but is separate for
00053 // no real reason.
00054 
00055 class hold_globals
00056 {
00057 public:
00058   hold_globals();
00059   ~hold_globals();
00060   
00061   // data accessed by the threads...
00062 
00063   time_stamp when_to_exit; // when the program should vamoose by.
00064   bool sleep_mode;  // are we in sleepy mode where we don't create new threads?
00065   reader_writer_lock protection;
00066     // the synchronization mechanism we're testing.
00067   bool exit_now;  // true if the program is in a shutting down state.
00068   int thread_count;
00069     // a variable that will be used to track how many threads are active.
00070   protectee safe_guy;
00071     // the object protected by our lock.
00072 };
00073 
00074 hold_globals::hold_globals()
00075 : when_to_exit(DEFAULT_LIFE_TIME),
00076 //hmmm: get time to run from command line.
00077   sleep_mode(false),
00078   protection(),
00079   exit_now(false),
00080   thread_count(0),
00081   safe_guy(0)
00082 {
00083 }
00084 
00085 hold_globals::~hold_globals()
00086 {
00087 }
00088 
00089 static hold_globals global_data;
00090 
00092 
00093 // forward.
00094 class test_rw_lock;
00095 
00096 // the thread that keeps the test doing stuff occasionally.
00097 class rw_lock_thread : public ithread
00098 {
00099 public:
00100   test_rw_lock &_parent;  // the main class.
00101   rw_lock_thread(test_rw_lock &parent) : ithread(100), _parent(parent) {}
00102   void perform_activity(void *unused);
00103 };
00104 
00105 // the thread that adds to the thingy.
00106 class adder_thread : public ithread
00107 {
00108 public:
00109   test_rw_lock &_parent;
00110   adder_thread(test_rw_lock &parent) : ithread(), _parent(parent) {}
00111   void perform_activity(void *non);
00112 };
00113 
00114 class checker_thread : public ithread
00115 {
00116 public:
00117   test_rw_lock &_parent;
00118   checker_thread(test_rw_lock &parent) : ithread(), _parent(parent) {}
00119   void perform_activity(void *non);
00120 };
00121 
00123 
00124 class test_rw_lock : public application_shell
00125 {
00126 public:
00127   test_rw_lock() : application_shell("t_rw_lock"), _periodic(*this) {}
00128 
00129   IMPLEMENT_CLASS_NAME("test_rw_lock");
00130 
00131   virtual ~test_rw_lock() {
00132     _periodic.stop();
00133     for (int i = 0; i < _threads.length(); i++) _threads[i]->stop();
00134     for (int k = 0; k < _threads.length(); k++) WHACK(_threads[k]);
00135   }
00136 
00137   int execute();
00138 
00139   void add_thread(ithread *to_add);
00140     // the thread "to_add" is added to our list and started.
00141 
00142 private:
00143   rw_lock_thread _periodic;  // drives the periodic activities here.
00144   mutex _list_lock;  // protects the threads list.
00145   array<ithread *> _threads;  // tracks our guys.
00146 };
00147 
00149 
00150 #undef LOG
00151 #ifdef DEBUG_RW_LOCK
00152   // our thread's macro for logging with a timestamp.
00153   #define LOG(to_print) _parent.log(timestamp(true) + istring(to_print))
00154 #else
00155   #define LOG(to_print) {}
00156 #endif
00157 
00158 void rw_lock_thread::perform_activity(void *formal(unused)) {
00159   // make sure it's not time to leave.
00160   if (global_data.when_to_exit < time_stamp()) {
00161     // get out now.
00162 //      PostMessage(WM_CLOSE);
00163     cancel();
00164     return;
00165   }
00166 
00167   // do something random to the list.
00168   if (global_data.sleep_mode) {
00169     // we have a chance to get out of sleep mode next time around.
00170 LOG("main loop zzzz...");
00171     if (rando.inclusive(1, 100) > 95) global_data.sleep_mode = false;
00172     if (!global_data.sleep_mode) { LOG("<waking up.>"); }
00173   } else if (rando.inclusive(0, 100) == 58) {
00174     // we hit the magical sleeper period.
00175     global_data.sleep_mode = true;
00176     LOG("<going to sleep.>");
00177   } else {
00178     int selector = rando.inclusive(1, 100);
00179     // there's currently a 15% chance of adding an item and a 6% chance of
00180     // removing an item.
00181     if ( (selector >= 14) && (selector <= 28) ) {
00182       LOG("launching an adder.");
00183       _parent.add_thread(new adder_thread(_parent));
00184 //    } else if ( (selector >= 69) && (selector <= 74) ) {
00185 //       LOG("launching a deleter.");
00186 //      _beginthread(deleter_thread, 0, this);
00187     }
00188 //hmmm: add more types of threads we could call.
00189   }
00190 }
00191 
00193 
00194 void adder_thread::perform_activity(void *)
00195 {
00197   safe_add(global_data.thread_count, 1);
00198 LOG(istring(istring::SPRINTF, "adder is thread %d.", global_data.thread_count));
00199 
00200   global_data.protection.begin_write();
00201   global_data.safe_guy++;
00202 LOG(istring(istring::SPRINTF, "in write: adder incrementing to %d.", global_data.safe_guy));
00203   global_data.protection.end_write();
00204 
00205 LOG("exiting adder");
00206   safe_add(global_data.thread_count, -1);
00207 }
00208 
00210 
00211 void checker_thread::perform_activity(void *) {
00212   safe_add(global_data.thread_count, 1);
00213 LOG(istring(istring::SPRINTF, "checker is thread %d.", global_data.thread_count));
00214 
00215   protectee val = 0;
00216 
00217   while (!global_data.exit_now) {
00218     // test the invariant that the value is always increasing.
00219     global_data.protection.begin_read();
00220 LOG("in reader: got read access.");
00221     if (global_data.safe_guy < val)
00222       deadly_error("checker", "test", "value was not increasing!");
00223     val = global_data.safe_guy;
00224 LOG("in reader: giving up read access.");
00225     global_data.protection.end_read();
00226 LOG("in reader: after gave up read access.");
00227 
00228     int milliseconds_to_sleep = rando.inclusive(200, 828);
00229     portable::sleep_ms(milliseconds_to_sleep);
00230 //testing.
00231 
00232 //    time_stamp wake_up_time(milliseconds_to_sleep);
00233 //      // pause the thread until it's time to wake up (or leave).
00234 //    while (!global_data.exit_now && (time_stamp() < wake_up_time)) {
00235 //      portable::sleep_ms(0);
00236 //    }
00237   }
00238   LOG("exiting checker.");
00239   safe_add(global_data.thread_count, -1);
00240 }
00241 
00243 
00244 #undef LOG
00245 #ifdef DEBUG_RW_LOCK
00246   #define LOG(to_print) log(timestamp(true) + istring(to_print))
00247 #else
00248   #define LOG(to_print) {}
00249 #endif
00250 
00251 void test_rw_lock::add_thread(ithread *to_add)
00252 {
00253   auto_synchronizer l(_list_lock);
00254   _threads += to_add;
00255   to_add->start(NIL);
00256 }
00257 
00258 int test_rw_lock::execute()
00259 {
00260   log(""); log(""); log("");  // some blank lines.
00261   LOG("starting test.");
00262   log("testing in progress...  please hang on.");
00263 
00264   _periodic.start(NIL);
00265 
00266   // start the threads that check list integrity.
00267   for (int i = 0; i < NUMBER_OF_CHECKERS; i++)
00268     _threads += new checker_thread(*this);
00269 
00270   while (time_stamp() < global_data.when_to_exit) {
00271     // not goodbye yet.
00272     portable::sleep_ms(42);
00273   }
00274 
00275   global_data.exit_now = true;  // set the thread go bye-bye state.
00276 
00277   // wait until all the threads have exited.
00278   LOG("now exiting from all threads...");
00279   while (global_data.thread_count > 0) { portable::sleep_ms(0); }
00280   LOG("done exiting from all threads.");
00281 
00282   LOG("ending test.");
00283   _periodic.stop();
00284   log("test completed.");
00285 
00286   guards::alert_message("reader_writer_lock:: works for those functions tested.");
00287   return 0;
00288 }
00289 
00291 
00292 int main(int formal(argc), char *formal(argv)[])
00293 {
00294   test_rw_lock tester;
00295   return tester.execute();
00296 }
00297 

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