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 <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
00039
00040
00041 const int DEFAULT_LIFE_TIME = 4 * MINUTE_ms;
00042
00043
00044 chaos rando;
00045
00046
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
00063
00064 static int thread_count = 0;
00065
00066
00067 const istring LOGFILE_NAME = path_configuration::make_logfile_name
00068 ("t_safe_list.log");
00069
00070
00071 #define LOG(to_print) log.log(timestamp(true) + istring(to_print))
00072
00073
00074 const int NUMBER_OF_CHECKERS = 8;
00075
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
00087 file_logger log(LOGFILE_NAME);
00088 LOG(istring(istring::SPRINTF, "adder is thread %d.", thread_count));
00089
00090
00091 safe_list_write_iterator *iter = _the_list.open_writer();
00092
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
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
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
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
00164
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
00169 if (!_the_list.empty(*iter)) {
00170
00171 int bigger = CASTER(iter->observe())->operator int();
00172
00173 while (!iter->is_head()) {
00174
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
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
00203
00204
00205 file_logger log(LOGFILE_NAME);
00206 log.log(""); log.log(""); log.log("");
00207 LOG("starting test.");
00208
00209 amorph<ithread> thread_list;
00210
00211
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
00217 time_stamp run_until(time_to_run);
00218
00219 while (run_until > time_stamp()) {
00220
00221 if (sleep_time) {
00222 LOG("main loop zzzz...");
00223 portable::sleep_ms(84);
00224
00225 if (rando.inclusive(1, 100) > 95) {
00226 sleep_time = false;
00227 LOG("<waking up.>");
00228 exit_now = false;
00229 }
00230 continue;
00231 }
00232 if (rando.inclusive(0, 100) == 58) {
00233
00234 sleep_time = true;
00235 LOG("<going to sleep.>");
00236 exit_now = true;
00237 continue;
00238 }
00239 int selector = rando.inclusive(1, 100);
00240
00241
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
00253 }
00254
00255
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();
00281
00282 guards::alert_message("safe_list:: works for those functions tested.");
00283 return 0;
00284 }
00285