00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
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
00034
00035 const int DEFAULT_FISH = 32;
00036
00037
00038 const int DEFAULT_RUN_TIME = 20 * SECOND_ms;
00039
00040
00041 const int THREAD_PAUSE_LOWEST = 0;
00042 const int THREAD_PAUSE_HIGHEST = 48;
00043
00044
00045
00046 const int MIN_SAME_THREAD_LOCKING_TESTS = 100;
00047 const int MAX_SAME_THREAD_LOCKING_TESTS = 1000;
00048
00049
00050 int concurrent_biters = 0;
00051
00052
00053 int grab_lock = 0;
00054
00055
00056 mutex &guard() { static mutex _muttini; return _muttini; }
00057
00058
00059
00060 istring protected_string;
00061
00062
00063 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger(), to_print)
00064
00065
00066
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++;
00077 } else {
00078 if (locked > 0) {
00079
00080 guard().unlock();
00081 locked--;
00082 }
00083 }
00084 }
00085 for (int j = 0; j < locked; j++) {
00086
00087 guard().unlock();
00088 }
00089 }
00090
00091
00092
00093
00094 class piranha : public ithread
00095 {
00096 public:
00097 chaos _rando;
00098
00099 piranha() : ithread(0) {
00100 FUNCDEF("constructor");
00101 safe_add(concurrent_biters, 1);
00102
00103 }
00104
00105 virtual ~piranha() {
00106 FUNCDEF("destructor");
00107 safe_add(concurrent_biters, -1);
00108
00109 }
00110
00111 IMPLEMENT_CLASS_NAME("piranha");
00112
00113 void perform_activity(void *formal(data)) {
00114 FUNCDEF("perform_activity");
00115
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
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;
00137
00138 barracuda() : ithread(0) {
00139 FUNCDEF("constructor");
00140 safe_add(concurrent_biters, 1);
00141
00142 }
00143
00144 virtual ~barracuda() {
00145 FUNCDEF("destructor");
00146 safe_add(concurrent_biters, -1);
00147
00148 }
00149
00150 IMPLEMENT_CLASS_NAME("barracuda");
00151
00152 void perform_activity(void *formal(data)) {
00153 FUNCDEF("perform_activity");
00154
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
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;
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
00191
00192
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
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
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
00231
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();
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