t_mutex.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : test_mutex                                                        *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 1994-$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 <basis/mutex.h>
00019 #include <data_struct/amorph.cpp>
00020 #include <mechanisms/ithread.h>
00021 #include <mechanisms/safe_roller.h>
00022 #include <mechanisms/time_stamp.h>
00023 #include <opsystem/application_shell.h>
00024 #include <loggers/console_logger.h>
00025 #include <opsystem/path_configuration.h>
00026 #include <data_struct/static_memory_gremlin.h>
00027 
00028 #ifdef __WIN32__
00029   #include <process.h>
00030 #endif
00031 
00032 const int MAX_MUTEX_TIMING_TEST = 2000000;
00033   // the number of times we'll lock and unlock a mutex.
00034 
00035 const int DEFAULT_FISH = 32;
00036   // the number of threads, by default.
00037 
00038 const int DEFAULT_RUN_TIME = 20 * SECOND_ms;
00039   // the length of time to run the program.
00040 
00041 const int THREAD_PAUSE_LOWEST = 0;
00042 const int THREAD_PAUSE_HIGHEST = 48;
00043   // this is the range of random sleeps that a thread will take after
00044   // performing it's actions.
00045 
00046 const int MIN_SAME_THREAD_LOCKING_TESTS = 100;
00047 const int MAX_SAME_THREAD_LOCKING_TESTS = 1000;
00048   // the range of times we'll test recursively locking the mutex.
00049 
00050 int concurrent_biters = 0;
00051   // the number of threads that are currently active.
00052 
00053 int grab_lock = 0;
00054   // this is upped whenever a fish obtains access to the mutex.
00055 
00056 mutex &guard() { static mutex _muttini; return _muttini; }
00057   // the guard ensures that the grab lock isn't molested by two fish at
00058   // once... hopefully.
00059 
00060 istring protected_string;
00061   // this string is protected only by the mutex of guard().
00062 
00063 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger(), to_print)
00064   // our macro for logging with a timestamp.
00065 
00066 // expects guardian mutex to already be locked once when coming in.
00067 void test_recursive_locking(chaos &_rando)
00068 {
00069   int test_attempts = _rando.inclusive(MIN_SAME_THREAD_LOCKING_TESTS,
00070       MAX_SAME_THREAD_LOCKING_TESTS);
00071   int locked = 0;
00072   for (int i = 0; i < test_attempts; i++) {
00073     bool lock = !!(_rando.inclusive(0, 1));
00074     if (lock) {
00075       guard().lock();
00076       locked++;  // one more lock.
00077     } else {
00078       if (locked > 0) {
00079         // must be sure we are not already locally unlocked completely.
00080         guard().unlock();
00081         locked--;
00082       }
00083     }
00084   }
00085   for (int j = 0; j < locked; j++) {
00086     // drop any locks we had left during the test.
00087     guard().unlock();
00088   }
00089 }
00090 
00091 //hmmm: how are these threads different so far?  they seem to do exactly
00092 //      the same thing.  maybe one should eat chars from the string.
00093 
00094 class piranha : public ithread
00095 {
00096 public:
00097   chaos _rando;  // our randomizer.
00098 
00099   piranha() : ithread(0) {
00100     FUNCDEF("constructor");
00101     safe_add(concurrent_biters, 1);
00102 //LOG(istring(istring::SPRINTF, "there are %d biters.", concurrent_biters));
00103   }
00104 
00105   virtual ~piranha() {
00106     FUNCDEF("destructor");
00107     safe_add(concurrent_biters, -1); 
00108 //LOG("reached piranha destructor.");
00109   }
00110 
00111   IMPLEMENT_CLASS_NAME("piranha");
00112 
00113   void perform_activity(void *formal(data)) {
00114     FUNCDEF("perform_activity");
00115     // we grab the lock.
00116     guard().lock();
00117     safe_add(grab_lock, 1);
00118     if (grab_lock > 1)
00119       deadly_error(class_name(), func, "grab lock was already active");
00120     protected_string += char(_rando.inclusive('a', 'z'));
00121 
00122     test_recursive_locking(_rando);
00123 
00124     safe_add(grab_lock, -1);
00125     guard().unlock();
00126     // dropped the lock.  snooze a bit.
00127     if (!should_stop())
00128       portable::sleep_ms(_rando.inclusive(THREAD_PAUSE_LOWEST, THREAD_PAUSE_HIGHEST));
00129   }
00130 
00131 };
00132 
00133 class barracuda : public ithread
00134 {
00135 public:
00136   chaos _rando;  // our randomizer.
00137 
00138   barracuda() : ithread(0) {
00139     FUNCDEF("constructor");
00140     safe_add(concurrent_biters, 1);
00141 //LOG(istring(istring::SPRINTF, "there are %d biters.", concurrent_biters));
00142   }
00143 
00144   virtual ~barracuda() {
00145     FUNCDEF("destructor");
00146      safe_add(concurrent_biters, -1);
00147 //LOG("reached barracuda destructor.");
00148   }
00149 
00150   IMPLEMENT_CLASS_NAME("barracuda");
00151 
00152   void perform_activity(void *formal(data)) {
00153     FUNCDEF("perform_activity");
00154     // we grab the lock.
00155     guard().lock();
00156     safe_add(grab_lock, 1);
00157     if (grab_lock > 1)
00158       deadly_error(class_name(), func, "grab lock was already active");
00159 
00160     test_recursive_locking(_rando);
00161 
00162     protected_string += char(_rando.inclusive('a', 'z'));
00163     safe_add(grab_lock, -1);
00164     guard().unlock();
00165     // done with the lock.  sleep for a while.
00166     if (!should_stop())
00167       portable::sleep_ms(_rando.inclusive(THREAD_PAUSE_LOWEST, THREAD_PAUSE_HIGHEST));
00168   }
00169 };
00170 
00172 
00173 class test_mutex : public application_shell
00174 {
00175 public:
00176   chaos _rando;  // our randomizer.
00177 
00178   test_mutex() : application_shell(class_name()) {}
00179 
00180   IMPLEMENT_CLASS_NAME("test_mutex");
00181 
00182   int execute();
00183 };
00184 
00185 int test_mutex::execute()
00186 {
00187   FUNCDEF("execute");
00188 
00189   {
00190     // we check how long a lock and unlock of a non-locked mutex will take.
00191     // this is important to know so that we aren't spending much of our time
00192     // locking mutexes just due to the mechanism.
00193     mutex ted;
00194     time_stamp mutt_in;
00195     for (int qb = 0; qb < MAX_MUTEX_TIMING_TEST; qb++) {
00196       ted.lock();
00197       ted.unlock();
00198     }
00199     time_stamp mutt_out;
00200     log(isprintf("%d mutex lock & unlock pairs took %f seconds,",
00201         MAX_MUTEX_TIMING_TEST,
00202         (mutt_out.value() - mutt_in.value()) / SECOND_ms));
00203     log(isprintf("or %f ms per (lock+unlock).",
00204         (mutt_out.value() - mutt_in.value()) / MAX_MUTEX_TIMING_TEST));
00205   }
00206 
00207   // make sure the guard is initialized before the threads run.
00208   guard().lock();
00209   guard().unlock();
00210 
00211   amorph<ithread> thread_list;
00212 
00213   for (int i = 0; i < DEFAULT_FISH; i++) {
00214     ithread *t = NIL;
00215     if (i % 2) t = new piranha;
00216     else t = new barracuda;
00217     thread_list.append(t);
00218     ithread *q = thread_list[thread_list.elements() - 1];
00219     if (q != t)
00220       deadly_error(class_name(), func, "amorph has incorrect pointer!");
00221     // start the thread we added.
00222     thread_list[thread_list.elements() - 1]->start(NIL);
00223   }
00224 
00225   time_stamp when_to_leave(DEFAULT_RUN_TIME);
00226   while (when_to_leave > time_stamp()) {
00227     portable::sleep_ms(100);
00228   }
00229 
00230 //hmmm: try just resetting the amorph;
00231 //      that should work fine.
00232 
00233   LOG("now cancelling all threads....");
00234 
00235   for (int j = 0; j < thread_list.elements(); j++) thread_list[j]->cancel();
00236 
00237   LOG("now stopping all threads....");
00238 
00239   for (int k = 0; k < thread_list.elements(); k++) thread_list[k]->stop();
00240 
00241   LOG("resetting thread list....");
00242 
00243   thread_list.reset();  // should whack all threads.
00244 
00245   if (concurrent_biters != 0)
00246     deadly_error(class_name(), func, "threads were still active supposedly!");
00247 
00248   LOG("done exiting from all threads....");
00249 
00250   LOG(istring(istring::SPRINTF, "the accumulated string had %d characters "
00251       "which means\nthere were %d thread activations from %d threads.",
00252       protected_string.length(), protected_string.length(),
00253       DEFAULT_FISH));
00254 
00255   guards::alert_message("mutex:: works for all functions tested.");
00256   return 0;
00257 }
00258 
00259 HOOPLE_MAIN(test_mutex, )
00260 

Generated on Fri Nov 28 04:29:35 2008 for HOOPLE Libraries by  doxygen 1.5.1