test_mutex.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include <application/hoople_main.h>
00016 #include <basis/astring.h>
00017 #include <basis/guards.h>
00018 #include <basis/mutex.h>
00019 #include <configuration/application_configuration.h>
00020 #include <loggers/critical_events.h>
00021 #include <loggers/program_wide_logger.h>
00022 #include <mathematics/chaos.h>
00023 #include <processes/ethread.h>
00024 #include <processes/safe_roller.h>
00025 #include <structures/amorph.h>
00026 #include <structures/static_memory_gremlin.h>
00027 #include <timely/time_control.h>
00028 #include <timely/time_stamp.h>
00029 #include <unit_test/unit_base.h>
00030
00031 #ifdef __WIN32__
00032 #include <process.h>
00033 #endif
00034
00035 using namespace application;
00036 using namespace basis;
00037 using namespace loggers;
00038 using namespace mathematics;
00039 using namespace timely;
00040 using namespace processes;
00041 using namespace structures;
00042 using namespace unit_test;
00043
00044
00045
00046
00047 const int MAX_MUTEX_TIMING_TEST = 2000000;
00048
00049
00050 const int DEFAULT_FISH = 32;
00051
00052
00053 const int DEFAULT_RUN_TIME = 2 * SECOND_ms;
00054
00055
00056 const int THREAD_PAUSE_LOWEST = 0;
00057 const int THREAD_PAUSE_HIGHEST = 48;
00058
00059
00060
00061 const int MIN_SAME_THREAD_LOCKING_TESTS = 100;
00062 const int MAX_SAME_THREAD_LOCKING_TESTS = 1000;
00063
00064
00065 int concurrent_biters = 0;
00066
00067
00068 int grab_lock = 0;
00069
00070
00071 mutex &guard() { static mutex _muttini; return _muttini; }
00072
00073
00074
00075 astring protected_string;
00076
00077
00078 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
00079
00080
00081
00082 void test_recursive_locking(chaos &_rando)
00083 {
00084 int test_attempts = _rando.inclusive(MIN_SAME_THREAD_LOCKING_TESTS,
00085 MAX_SAME_THREAD_LOCKING_TESTS);
00086 int locked = 0;
00087 for (int i = 0; i < test_attempts; i++) {
00088 bool lock = !!(_rando.inclusive(0, 1));
00089 if (lock) {
00090 guard().lock();
00091 locked++;
00092 } else {
00093 if (locked > 0) {
00094
00095 guard().unlock();
00096 locked--;
00097 }
00098 }
00099 }
00100 for (int j = 0; j < locked; j++) {
00101
00102 guard().unlock();
00103 }
00104 }
00105
00106
00107
00108
00109 #undef UNIT_BASE_THIS_OBJECT
00110 #define UNIT_BASE_THIS_OBJECT c_testing
00111
00112 class piranha : public ethread
00113 {
00114 public:
00115 chaos _rando;
00116 unit_base &c_testing;
00117
00118 piranha(unit_base &testing) : ethread(0), c_testing(testing) {
00119 FUNCDEF("constructor");
00120 safe_add(concurrent_biters, 1);
00121 ASSERT_TRUE(concurrent_biters >= 1, "the piranha is very noticeable");
00122
00123 }
00124
00125 virtual ~piranha() {
00126 FUNCDEF("destructor");
00127 safe_add(concurrent_biters, -1);
00128
00129 }
00130
00131 DEFINE_CLASS_NAME("piranha");
00132
00133 void perform_activity(void *formal(data)) {
00134 FUNCDEF("perform_activity");
00135 {
00136
00137 auto_synchronizer locked(guard());
00138
00139 ASSERT_TRUE(&locked != NIL, "auto_synchronizer should grab the mutex object's lock");
00140
00141
00142 safe_add(grab_lock, 1);
00143 ASSERT_TRUE(grab_lock <= 1, "grab lock should not already be active");
00144 protected_string += char(_rando.inclusive('a', 'z'));
00145
00146 test_recursive_locking(_rando);
00147
00148 safe_add(grab_lock, -1);
00149 }
00150
00151 if (!should_stop())
00152 time_control::sleep_ms(_rando.inclusive(THREAD_PAUSE_LOWEST, THREAD_PAUSE_HIGHEST));
00153 }
00154
00155 };
00156
00157 class barracuda : public ethread
00158 {
00159 public:
00160 chaos _rando;
00161 unit_base &c_testing;
00162
00163 barracuda(unit_base &testing) : ethread(0), c_testing(testing) {
00164 FUNCDEF("constructor");
00165 safe_add(concurrent_biters, 1);
00166 ASSERT_TRUE(concurrent_biters >= 1, "our presence should have been noticed");
00167
00168 }
00169
00170 virtual ~barracuda() {
00171 FUNCDEF("destructor");
00172 safe_add(concurrent_biters, -1);
00173
00174 }
00175
00176 DEFINE_CLASS_NAME("barracuda");
00177
00178 void perform_activity(void *formal(data)) {
00179 FUNCDEF("perform_activity");
00180
00181 guard().lock();
00182 safe_add(grab_lock, 1);
00183 ASSERT_TRUE(grab_lock <= 1, "grab lock should not already be active");
00184
00185 test_recursive_locking(_rando);
00186
00187 protected_string += char(_rando.inclusive('a', 'z'));
00188 safe_add(grab_lock, -1);
00189 guard().unlock();
00190
00191 if (!should_stop())
00192 time_control::sleep_ms(_rando.inclusive(THREAD_PAUSE_LOWEST, THREAD_PAUSE_HIGHEST));
00193 }
00194 };
00195
00197
00198 #undef UNIT_BASE_THIS_OBJECT
00199 #define UNIT_BASE_THIS_OBJECT (*this)
00200
00201 class test_mutex : virtual public unit_base, virtual public application_shell
00202 {
00203 public:
00204 chaos _rando;
00205
00206 test_mutex() : application_shell() {}
00207
00208 DEFINE_CLASS_NAME("test_mutex");
00209
00210 int execute();
00211 };
00212
00213 int test_mutex::execute()
00214 {
00215 FUNCDEF("execute");
00216
00217 {
00218
00219
00220
00221 mutex ted;
00222 time_stamp mutt_in;
00223 for (int qb = 0; qb < MAX_MUTEX_TIMING_TEST; qb++) {
00224 ted.lock();
00225 ted.unlock();
00226 }
00227 time_stamp mutt_out;
00228 #ifdef DEBUG_MUTEX
00229 log(a_sprintf("%d mutex lock & unlock pairs took %f seconds,",
00230 MAX_MUTEX_TIMING_TEST,
00231 (mutt_out.value() - mutt_in.value()) / SECOND_ms));
00232 log(a_sprintf("or %f ms per (lock+unlock).",
00233 (mutt_out.value() - mutt_in.value()) / MAX_MUTEX_TIMING_TEST));
00234 #endif
00235 }
00236
00237
00238 guard().lock();
00239 guard().unlock();
00240
00241 amorph<ethread> thread_list;
00242
00243 for (int i = 0; i < DEFAULT_FISH; i++) {
00244 ethread *t = NIL;
00245 if (i % 2) t = new piranha(*this);
00246 else t = new barracuda(*this);
00247 thread_list.append(t);
00248 ethread *q = thread_list[thread_list.elements() - 1];
00249 ASSERT_EQUAL(q, t, "amorph pointer equivalence is required");
00250
00251 thread_list[thread_list.elements() - 1]->start(NIL);
00252 }
00253
00254 time_stamp when_to_leave(DEFAULT_RUN_TIME);
00255 while (when_to_leave > time_stamp()) {
00256 time_control::sleep_ms(100);
00257 }
00258
00259
00260
00261
00262 #ifdef DEBUG_MUTEX
00263 LOG("now cancelling all threads....");
00264 #endif
00265
00266 for (int j = 0; j < thread_list.elements(); j++) thread_list[j]->cancel();
00267
00268 #ifdef DEBUG_MUTEX
00269 LOG("now stopping all threads....");
00270 #endif
00271
00272 for (int k = 0; k < thread_list.elements(); k++) thread_list[k]->stop();
00273
00274 int threads_active = 0;
00275 for (int k = 0; k < thread_list.elements(); k++) {
00276 if (thread_list[k]->thread_active()) threads_active++;
00277 }
00278 ASSERT_EQUAL(threads_active, 0, "threads should actually have stopped by now");
00279
00280 #ifdef DEBUG_MUTEX
00281 LOG("resetting thread list....");
00282 #endif
00283
00284 thread_list.reset();
00285
00286 ASSERT_EQUAL(concurrent_biters, 0, "threads should all be gone by now");
00287
00288 #ifdef DEBUG_MUTEX
00289 LOG("done exiting from all threads....");
00290
00291 LOG(astring(astring::SPRINTF, "the accumulated string had %d characters "
00292 "which means\nthere were %d thread activations from %d threads.",
00293 protected_string.length(), protected_string.length(),
00294 DEFAULT_FISH));
00295 #endif
00296
00297 return final_report();
00298 }
00299
00300 HOOPLE_MAIN(test_mutex, )
00301