00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
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 <basis/portable.h>
00021 #include <data_struct/unique_id.h>
00022 #include <mechanisms/ithread.h>
00023 #include <mechanisms/safe_roller.h>
00024 #include <mechanisms/time_stamp.h>
00025 #include <nodes/node.h>
00026 #include <nodes/catalogable.h>
00027 #include <nodes/object_catalog.h>
00028 #include <opsystem/application_shell.h>
00029 #include <loggers/console_logger.h>
00030 #include <loggers/file_logger.h>
00031 #include <opsystem/path_configuration.h>
00032 #include <data_struct/static_memory_gremlin.h>
00033 #include <textual/string_manipulation.h>
00034
00035 using namespace nodes;
00036
00037
00038
00039
00040 const int DEFAULT_LIFE_TIME = 7 * MINUTE_ms;
00041
00042
00043
00044 const int SHELL_SLEEP = 100;
00045
00046
00047
00048 const int PRESIDENT_SLEEP = 142;
00049
00050
00051 const int LOGGING_INTERVAL = 500;
00052
00053
00054
00055 #define LOG(to_print) program_wide_logger().log(timestamp(true) \
00056 + istring(to_print))
00057 #ifdef DEBUG_OBJECT_CATALOG
00058 #define MAYBE_LOG(to_print) LOG(to_print)
00059 #else
00060 #define MAYBE_LOG(to_print) {}
00061 #endif
00062
00064
00065 chaos rando;
00066
00067 istring random_name()
00068 {
00069 static mutex muttzilla;
00070 auto_synchronizer l(muttzilla);
00071 return string_manipulation::make_random_name(4, 28);
00072 }
00073
00074 static safe_roller rollo(1, MAXINT - 2);
00075
00077
00078 static int president_thread_id = 0;
00079
00080
00081
00082 static bool exit_now = false;
00083
00084
00085
00086 static object_catalog east_shelf;
00087 static object_catalog new_product_bin;
00088 static object_catalog north_shelf;
00089 static object_catalog trash_bin;
00090 static object_catalog west_shelf;
00091
00092
00093 static object_catalog *thread_bin = new object_catalog;
00094
00096
00097
00098
00099 class inventory_item : public nodes::catalogable
00100 {
00101 public:
00102 inventory_item(const unique_int &id, const istring &name, int count)
00103 : nodes::catalogable(id), _count(count), _name(name) {}
00104
00105 bool valid() const { return !!_name && (_count > 0); }
00106
00107 istring catalogable_name() const { return _name; }
00108
00109 istring text_form() const { return istring(istring::SPRINTF, "O: id %d, "
00110 "name %s, count %d", id().raw_id(), _name.s(), _count); }
00111
00112 int count() const { return _count; }
00113 void count(int new_count) { _count = new_count; }
00114 istring name() const { return _name; }
00115 void name(const istring &new_name) { _name = new_name; }
00116
00117 inventory_item *split_off(int to_split);
00118
00119
00120
00121
00122 bool join(inventory_item &to_join);
00123
00124
00125
00126
00127
00128 private:
00129 int _count;
00130 istring _name;
00131 };
00132
00134
00135 inventory_item *inventory_item::split_off(int to_split)
00136 {
00137 if (to_split <= 0) return NIL;
00138 if (to_split > count()) return NIL;
00139 inventory_item *to_return = new inventory_item(id(), "", 0);
00140 to_return->name(_name);
00141 to_return->count(to_split);
00142 count(count() - to_split);
00143 return to_return;
00144 }
00145
00146 bool inventory_item::join(inventory_item &to_join)
00147 {
00148 if (to_join.id() != id()) return false;
00149 if (to_join.name() != name()) return false;
00150 count(count() + to_join.count());
00151 to_join.count(0);
00152 to_join.name("");
00153 return true;
00154 }
00155
00157
00158 bool restock(object_catalog &list, inventory_item *to_add)
00159 {
00160 if (!to_add) return false;
00161 objcat_iterator *found = list.find(to_add->id());
00162 if (found) {
00163
00164 objcat_locket *l = found->open_locket();
00165 inventory_item *iti = dynamic_cast<inventory_item *>(&l->cat());
00166 bool ret = iti->join(*to_add);
00167 found->close_locket(l);
00168 list.iter_unlock(found);
00169 WHACK(to_add);
00170 return ret;
00171 }
00172 list.add(to_add);
00173 return true;
00174 }
00175
00177
00178 class stocker_thread : public ithread, public nodes::catalogable
00179 {
00180 public:
00181 stocker_thread(int sleep)
00182 : ithread(sleep), nodes::catalogable(rollo.next_id()) {}
00183 void perform_activity(void *thread_data);
00184 IMPLEMENT_CLASS_NAME("stocker_thread");
00185 istring catalogable_name() const
00186 { return istring(istring::SPRINTF, "stocker_%d", id().raw_id()); }
00187 };
00188
00189 void stocker_thread::perform_activity(void *)
00190 {
00191 int do_what = rando.inclusive(1, 100);
00192
00193
00194 if (do_what < 40) {
00195
00196 inventory_item *to_add = new inventory_item(rollo.next_id(), random_name(),
00197 rando.inclusive(1, 200));
00198 istring name = to_add->name();
00199 int which_bin = rando.inclusive(1, 3);
00200 switch (which_bin) {
00201 case 1: restock(east_shelf, to_add); break;
00202 case 2: restock(west_shelf, to_add); break;
00203 case 3: restock(north_shelf, to_add); break;
00204 }
00205 MAYBE_LOG(istring(istring::SPRINTF, "%s created a new \"%s\".",
00206 catalogable_name().s(), name.s()));
00207 } else {
00208
00209 }
00210 }
00211
00213
00214 class rearranger_thread : public ithread, public nodes::catalogable
00215 {
00216 public:
00217 rearranger_thread(int sleep)
00218 : ithread(sleep), nodes::catalogable(rollo.next_id()) {}
00219 void perform_activity(void *thread_data);
00220 IMPLEMENT_CLASS_NAME("rearranger_thread");
00221 istring catalogable_name() const
00222 { return istring(istring::SPRINTF, "rearranger_%d", id().raw_id()); }
00223 };
00224
00225 void rearranger_thread::perform_activity(void *)
00226 {
00227 if (rando.inclusive(1, 100) > 38) return;
00228 object_catalog *source = NIL;
00229 istring src_name;
00230 object_catalog *destination = NIL;
00231 istring dest_name;
00232 int src_bin = rando.inclusive(1, 4);
00233 switch (src_bin) {
00234 case 1: source = &west_shelf; src_name = "west_shelf"; break;
00235 case 2: source = &east_shelf; src_name = "east_shelf"; break;
00236 case 3: source = &north_shelf; src_name = "north_shelf"; break;
00237 case 4: source = &trash_bin; src_name = "trash_bin"; break;
00238 }
00239 int dest_bin = src_bin;
00240 while (src_bin == dest_bin) dest_bin = rando.inclusive(1, 4);
00241 switch (dest_bin) {
00242 case 1: destination = &west_shelf; dest_name = "west_shelf"; break;
00243 case 2: destination = &east_shelf; dest_name = "east_shelf"; break;
00244 case 3: destination = &north_shelf; dest_name = "north_shelf"; break;
00245 case 4: destination = &trash_bin; dest_name = "trash_bin"; break;
00246 }
00247 objcat_iterator *iter = source->find_head();
00248 int elems = source->elements(*iter);
00249 if (!elems) {
00250
00251 source->iter_unlock(iter);
00252 return;
00253 }
00254 int which = rando.inclusive(0, elems - 1);
00255 for (int i = which; i > 0; i--) {
00256 iter->next();
00257 if (iter->is_tail())
00258 deadly_error("test_object_catalog", "rearranger", "iter count is at "
00259 "tail unexpectedly");
00260 }
00261 if (!iter->cat())
00262 deadly_error("test_object_catalog", "rearranger", "iter is at nil node");
00263 unique_int id_to_get = iter->cat()->id();
00264 source->iter_unlock(iter);
00265 inventory_item *iti = dynamic_cast<inventory_item *>
00266 (source->acquire(id_to_get));
00267 if (!iti) {
00268
00269
00270 return;
00271 }
00272 istring name = iti->name();
00273 unique_int id = iti->id();
00274 restock(*destination, iti);
00275 MAYBE_LOG(istring(istring::SPRINTF, "%s moved item %s with id %d "
00276 "from %s to %s.", catalogable_name().s(), name.s(), id.raw_id(),
00277 src_name.s(), dest_name.s()));
00278 }
00279
00281
00282 class inspector_thread : public ithread, public nodes::catalogable
00283 {
00284 public:
00285 inspector_thread(int sleep)
00286 : ithread(sleep), nodes::catalogable(rollo.next_id()) {}
00287 void perform_activity(void *thread_data);
00288 IMPLEMENT_CLASS_NAME("inspector_thread");
00289 istring catalogable_name() const
00290 { return istring(istring::SPRINTF, "inspector_%d", id().raw_id()); }
00291 };
00292
00293 void inspector_thread::perform_activity(void *)
00294 {
00295 if (rando.inclusive(1, 100) > 32) return;
00296 object_catalog *source = NIL;
00297 istring src_name;
00298 int src_bin = rando.inclusive(1, 4);
00299 switch (src_bin) {
00300 case 1: source = &west_shelf; src_name = "west_shelf"; break;
00301 case 2: source = &east_shelf; src_name = "east_shelf"; break;
00302 case 3: source = &north_shelf; src_name = "north_shelf"; break;
00303 case 4: source = &trash_bin; src_name = "trash_bin"; break;
00304 }
00305
00306 array<unique_int> inspection_list;
00307 objcat_iterator *iter = source->find_head();
00308 for ( ; !iter->is_tail(); iter->next())
00309 if (iter->cat()) inspection_list += iter->cat()->id();
00310 source->iter_unlock(iter);
00311
00312 for (int i = 0; i < inspection_list.length() * 4; i++) {
00313 int from = rando.inclusive(0, inspection_list.length() - 1);
00314 int to = rando.inclusive(0, inspection_list.length() - 1);
00315 unique_int intermediate = inspection_list[from];
00316 inspection_list[from] = inspection_list[to];
00317 inspection_list[to] = intermediate;
00318 }
00319 while (inspection_list.length()) {
00320 unique_int examine = inspection_list[0];
00321 inspection_list.zap(0, 0);
00322 objcat_iterator *found = source->find(examine);
00323 if (!found) continue;
00324 objcat_isolater *iso = source->separate(found);
00325 istring name = iso->cat().catalogable_name();
00326 int sleep = rando.inclusive(100, 380);
00327 portable::sleep_ms(sleep);
00328 source->rejoin(iso);
00329 MAYBE_LOG(istring(istring::SPRINTF, "%s inspected item %s with id %d "
00330 "from %s.", catalogable_name().s(), name.s(), examine.raw_id(),
00331 src_name.s()));
00332 if (should_stop()) return;
00333 }
00334 }
00335
00337
00338 class consumer_thread : public ithread, public nodes::catalogable
00339 {
00340 public:
00341 consumer_thread(int sleep)
00342 : ithread(sleep), nodes::catalogable(rollo.next_id()) {}
00343 void perform_activity(void *thread_data);
00344 IMPLEMENT_CLASS_NAME("consumer_thread");
00345 istring catalogable_name() const
00346 { return istring(istring::SPRINTF, "consumer_%d", id().raw_id()); }
00347 };
00348
00349
00350 struct purchase_record { int count; istring name; };
00351
00352 void consumer_thread::perform_activity(void *)
00353 {
00354 if (rando.inclusive(1, 100) > 28) return;
00355 object_catalog *source = NIL;
00356 istring src_name;
00357 int src_bin = rando.inclusive(1, 4);
00358 switch (src_bin) {
00359 case 1: source = &west_shelf; src_name = "west_shelf"; break;
00360 case 2: source = &east_shelf; src_name = "east_shelf"; break;
00361 case 3: source = &north_shelf; src_name = "north_shelf"; break;
00362 case 4: source = &trash_bin; src_name = "trash_bin"; break;
00363 }
00364 object_catalog shopping_basket;
00365 int purchase_count = rando.inclusive(0, 30);
00366 if (!purchase_count) return;
00367 while (purchase_count--) {
00368 objcat_iterator *iter = source->find_head();
00369 int elems = source->elements(*iter);
00370 if (!elems) {
00371
00372 source->iter_unlock(iter);
00373 break;
00374 }
00375 int which = rando.inclusive(0, elems - 1);
00376 for (int i = which; i > 0; i--) {
00377 iter->next();
00378 if (iter->is_tail())
00379 deadly_error("test_object_catalog", "consumer", "iter count is at "
00380 "tail unexpectedly");
00381 }
00382 if (!iter->cat())
00383 deadly_error("test_object_catalog", "consumer", "iter is at nil node");
00384 unique_int id_to_get = iter->cat()->id();
00385 objcat_locket *l = iter->open_locket();
00386 inventory_item *to_buy = dynamic_cast<inventory_item *>(&l->cat());
00387 inventory_item *purchase = to_buy->split_off(1);
00388 iter->close_locket(l);
00389 restock(shopping_basket, purchase);
00390 if (!to_buy->valid()) {
00391
00392 source->zap(*iter);
00393 iter->previous();
00394 }
00395 source->iter_unlock(iter);
00396 }
00397
00398 array<purchase_record> purchases;
00399 objcat_iterator *iter = shopping_basket.find_head();
00400 int elems = shopping_basket.elements(*iter);
00401 for ( ; !iter->is_tail(); iter->next()) {
00402 if (!iter->cat()) break;
00403 const inventory_item *got = dynamic_cast<const inventory_item *>
00404 (iter->cat());
00405 purchase_record new_record;
00406 new_record.name = got->name();
00407 new_record.count = got->count();
00408 purchases += new_record;
00409 }
00410 shopping_basket.iter_unlock(iter);
00411
00412 if (elems) {
00413 istring info(istring::SPRINTF, "%s bought %d items:",
00414 catalogable_name().s(), elems);
00415 for (int i = 0; i < purchases.length(); i++)
00416 info += istring(istring::SPRINTF, " %d of %s,", purchases[i].count,
00417 purchases[i].name.s());
00418 info.zap(info.end(), info.end());
00419 info += ".";
00420 MAYBE_LOG(info);
00421 }
00422
00423
00424 iter = shopping_basket.find_head();
00425 for ( ; !iter->is_tail(); iter->next()) {
00426 shopping_basket.zap(*iter);
00427 iter->previous();
00428 }
00429 shopping_basket.iter_unlock(iter);
00430 }
00431
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00482
00483 class president_thread : public ithread, public nodes::catalogable
00484 {
00485 public:
00486 president_thread()
00487 : ithread(PRESIDENT_SLEEP), nodes::catalogable(rollo.next_id())
00488 { president_thread_id = id().raw_id(); }
00489 void perform_activity(void *thread_data);
00490 IMPLEMENT_CLASS_NAME("president_thread");
00491 istring catalogable_name() const { return "president"; }
00492
00493 void fire_an_employee();
00494
00495
00496 void close_up_shop();
00497
00498 };
00499
00500 void president_thread::perform_activity(void *)
00501 {
00502 if (exit_now) {
00503
00504 close_up_shop();
00505 return;
00506 }
00507
00508 int do_what = rando.inclusive(1, 100);
00509
00510 if (do_what < 10) {
00511
00512 int sleep = rando.inclusive(60, 180);
00513 stocker_thread *new_stocker = new stocker_thread(sleep);
00514 thread_bin->add(new_stocker);
00515 new_stocker->start(this);
00516 MAYBE_LOG(istring(istring::SPRINTF, "%s started a new stocker.",
00517 catalogable_name().s()));
00518 } else if (do_what < 20) {
00519
00520 int sleep = rando.inclusive(60, 180);
00521 consumer_thread *new_consumer = new consumer_thread(sleep);
00522 thread_bin->add(new_consumer);
00523 new_consumer->start(this);
00524 MAYBE_LOG(istring(istring::SPRINTF, "%s started a new consumer.",
00525 catalogable_name().s()));
00526 } else if (do_what < 30) {
00527
00528 int sleep = rando.inclusive(60, 180);
00529 rearranger_thread *new_rearranger = new rearranger_thread(sleep);
00530 thread_bin->add(new_rearranger);
00531 new_rearranger->start(this);
00532 MAYBE_LOG(istring(istring::SPRINTF, "%s started a new rearranger.",
00533 catalogable_name().s()));
00534
00535 } else if (do_what < 31) {
00536
00537 } else if (do_what < 32) {
00538
00539 } else if (do_what < 33) {
00540
00541 } else if (do_what < 50) {
00542
00543 int sleep = rando.inclusive(60, 180);
00544 inspector_thread *new_inspector = new inspector_thread(sleep);
00545 thread_bin->add(new_inspector);
00546 new_inspector->start(this);
00547 MAYBE_LOG(istring(istring::SPRINTF, "%s started a new inspector.",
00548 catalogable_name().s()));
00549 } else if (do_what < 69) {
00550
00551 fire_an_employee();
00552 } else {
00553
00554 }
00555
00556 objcat_iterator *iter = thread_bin->find_head();
00557 static int _num_threads = -1;
00558 if (_num_threads < 0)
00559 _num_threads = thread_bin->elements(*iter);
00560 if (thread_bin->elements(*iter) != _num_threads) {
00561 MAYBE_LOG(isprintf("%d threads active.", thread_bin->elements(*iter)));
00562 _num_threads = thread_bin->elements(*iter);
00563 }
00564 thread_bin->iter_unlock(iter);
00565 }
00566
00567 void president_thread::fire_an_employee()
00568 {
00569 objcat_iterator *iter = thread_bin->find_head();
00570 int elems = thread_bin->elements(*iter);
00571 if (elems < 1) {
00572
00573 thread_bin->iter_unlock(iter);
00574 return;
00575 }
00576
00577 int bye_bye = rando.inclusive(0, elems - 1);
00578 for (int i = bye_bye; i > 0; i--) {
00579 iter->next();
00580 if (iter->is_tail())
00581 deadly_error("test_object_catalog", "president", "iter count is at "
00582 "tail unexpectedly");
00583 }
00584 if (!iter->cat())
00585 deadly_error("test_object_catalog", "president", "iter at nil node");
00586 istring name = iter->cat()->catalogable_name();
00587 if (iter->cat()->id() == id()) {
00588
00589 thread_bin->iter_unlock(iter);
00590 return;
00591 }
00592
00593 objcat_locket *l = iter->open_locket();
00594 ithread *to_zap = dynamic_cast<ithread *>(&l->cat());
00595
00596 to_zap->stop();
00597 unique_int whack_id = iter->cat()->id();
00598 iter->close_locket(l);
00599 thread_bin->iter_unlock(iter);
00600
00601 thread_bin->zap_id(whack_id);
00602 MAYBE_LOG(istring(istring::SPRINTF, "president whacked %s.", name.s()));
00603 }
00604
00605 void president_thread::close_up_shop()
00606 {
00607
00608 static bool printed_shutdown_start = false;
00609 if (!printed_shutdown_start) {
00610 MAYBE_LOG("president shutting down all threads.");
00611 printed_shutdown_start = true;
00612 }
00613 objcat_iterator *iter = thread_bin->find_head();
00614 for ( ; !iter->is_tail(); iter->next()) {
00615
00616 if (!iter->cat()) continue;
00617 objcat_locket *l = iter->open_locket();
00618 ithread *t = dynamic_cast<ithread *>(&l->cat());
00619 if (t != (ithread *)this) t->cancel();
00620 iter->close_locket(l);
00621 }
00622
00623 iter->jump_head();
00624 for ( ; !iter->is_tail(); iter->next()) {
00625
00626 if (!iter->cat()) continue;
00627 const ithread *t = dynamic_cast<const ithread *>
00628 (iter->cat());
00629 if (t->thread_finished()) {
00630
00631 thread_bin->zap(*iter);
00632 iter->previous();
00633 }
00634 }
00635
00636
00637 iter->jump_head();
00638 bool others_remain = false;
00639 for ( ; !iter->is_tail(); iter->next()) {
00640
00641 if (!iter->cat()) continue;
00642 const ithread *t = dynamic_cast<const ithread *>
00643 (iter->cat());
00644 if (t != (ithread *)this) others_remain = true;
00645 }
00646 thread_bin->iter_unlock(iter);
00647 if (!others_remain) {
00648
00649 cancel();
00650 MAYBE_LOG("president has completely shut down all other threads.");
00651 }
00652 }
00653
00655
00656 class test_object_catalog : public application_shell
00657 {
00658 public:
00659 test_object_catalog();
00660 ~test_object_catalog();
00661
00662 IMPLEMENT_CLASS_NAME("test_object_catalog");
00663
00664 int execute();
00665
00666 private:
00667 };
00668
00670
00671 test_object_catalog::test_object_catalog()
00672 : application_shell("t_object_catalog")
00673 {}
00674
00675 test_object_catalog::~test_object_catalog() {}
00676
00677 int test_object_catalog::execute()
00678 {
00679 president_thread *the_president = new president_thread();
00680 the_president->start(this);
00681 thread_bin->add(the_president);
00682
00683 time_stamp run_until(DEFAULT_LIFE_TIME);
00684
00685
00686
00687 program_wide_logger().log("");
00688 program_wide_logger().log("");
00689 program_wide_logger().log("");
00690 LOG("starting test of object_catalog.");
00691
00692 while (run_until > time_stamp()) { portable::sleep_ms(SHELL_SLEEP); }
00693
00694
00695 LOG("now exiting from all threads...");
00696 exit_now = true;
00697 while (!the_president->thread_finished()) { portable::sleep_ms(42); }
00698 LOG("done exiting from all threads.");
00699
00700 objcat_iterator *iter = thread_bin->find_head();
00701 for ( ; !iter->is_tail(); iter->next()) {
00702 thread_bin->zap(*iter);
00703 iter->previous();
00704 }
00705 thread_bin->iter_unlock(iter);
00706
00707 LOG("exiting test of object_catalog.");
00708
00709 WHACK(thread_bin);
00710
00711
00712 guards::alert_message("object_catalog:: works for those functions tested.");
00713
00714 return 0;
00715 }
00716
00718
00719 HOOPLE_MAIN(test_object_catalog, )
00720