t_object_catalog.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : test_object_catalog                                               *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 1998-$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/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 //#define DEBUG_OBJECT_CATALOG
00038   // uncomment this line to get more debugging output.
00039 
00040 const int DEFAULT_LIFE_TIME = 7 * MINUTE_ms;
00041 //const int DEFAULT_LIFE_TIME = 4 * MINUTE_ms;
00042   // the amount of time the program runs by default.
00043 
00044 const int SHELL_SLEEP = 100;
00045   // the interval between the main shell's awakenings.  it will only check
00046   // whether it's time to leave at this rate.
00047 
00048 const int PRESIDENT_SLEEP = 142;
00049   // pause between calls of president, in ms.
00050 
00051 const int LOGGING_INTERVAL = 500;
00052   // the number of milliseconds between each dump of the log.
00053 
00054 // our macro for logging with a timestamp.
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;  // generates random numbers.
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   // this holds onto the president thread's identity so we can stop the whole
00080   // process when we want to.
00081 
00082 static bool exit_now = false;
00083   // true when the main shell decides the run time has elapsed.
00084 
00085 // these are the shelves and bins that the workers manage.
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 // here's where all the assistants are "stored".
00093 static object_catalog *thread_bin = new object_catalog;
00094 
00096 
00097 // the objects we're storing in the list.
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     // returns an inventory item by splitting out "to_split" items from this
00119     // inventory_item.  if there are not enough items to do that, the item
00120     // will be invalid.
00121 
00122   bool join(inventory_item &to_join);
00123     // merges the inventory_item in "to_join" with this one.  the item "to_join"
00124     // is drained of its count and invalidated.  this fails if "to_join" is
00125     // a different type of item than this; only items with the same name and
00126     // id can be joined.
00127 
00128 private:
00129   int _count;  // how many of this object there are.
00130   istring _name;  // the name of this type of object.
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     // it's an existing item that we need to merge.
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 //this might really be the inventor's task.  the buyer should look at
00193 //what's available to buy...
00194   if (do_what < 40) {
00195     // buy an item for inventory.
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     // drink coffee and eat doughnuts.
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;  // do nothing this time.
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     // nothing to move, so leave.
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     // could not acquire an item to move; this is probably not an error but
00269     // it is a bit rare.
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;  // do nothing this time.
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   // get the ids in this list.
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   // scramble the list of ids so we do them in unknown order.
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;  // moved or gone by now.
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 // this is a simple note of what was purchased.
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;  // do nothing this time.
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       // nothing to move, so leave.
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       // get rid of the empty item on the shelf.
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());  // remove last comma.
00419     info += ".";
00420     MAYBE_LOG(info);
00421   }
00422 
00423   // clean out what we bought now.
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 /* this might be avoided if we could figure out why the stupid names are
00435   coming up identical.
00436 class conjoiner_thread : public ithread, public nodes::catalogable
00437 {
00438 public:
00439   conjoiner_thread(int sleep)
00440       : nodes::catalogable(rollo.next_id()), ithread(sleep) {}
00441   void perform_activity(void *thread_data);
00442   istring catalogable_name() const
00443       { return istring(istring::SPRINTF, "conjoiner_%d", id().raw_id()); }
00444 };
00445 
00446 void conjoiner_thread::perform_activity(void *)
00447 {
00448   if (rando.inclusive(1, 100) > 48) return;  // do nothing this time.
00449   object_catalog *source = NIL;
00450   istring src_name;
00451   int src_bin = rando.inclusive(1, 4);
00452   switch (src_bin) {
00453     case 1: source = &west_shelf; src_name = "west_shelf"; break;
00454     case 2: source = &east_shelf; src_name = "east_shelf"; break;
00455     case 3: source = &north_shelf; src_name = "north_shelf"; break;
00456     case 4: source = &trash_bin; src_name = "trash_bin"; break;
00457   }
00458   int processed = 0;
00459   int maximum_allowed = rando.inclusive(20, 40);
00460   objcat_iterator *iter = source->find_head();
00461   for ( ; !iter->is_tail(); iter->next()) {
00462     if (!iter->cat()) break;
00463     catalogable *past_position = iter->cat();
00464     const inventory_item *got = dynamic_cast<const inventory_item *>
00465         (iter->cat());
00466     istring to_find = got->name();
00467     iter->next();  // skip the item we're at.
00468     for ( ; !iter->is_tail(); iter->next()) {
00469       // try to find other items with the same name, but different ids.
00470       const inventory_item *got = dynamic_cast<const inventory_item *>
00471           (iter->cat());
00472       if (got->name() == to_find) {
00473         // we have an entry with the same name as ours.
00474       }
00475     }
00476   }
00477   source->iter_unlock(iter);
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     // gets rid of one randomly chosen employee.
00495 
00496   void close_up_shop();
00497     // shuts down the business completely and clears out all of the workers.
00498 };
00499 
00500 void president_thread::perform_activity(void *)
00501 {
00502   if (exit_now) {
00503     // it's time for us to exit.
00504     close_up_shop();
00505     return;
00506   }
00507 
00508   int do_what = rando.inclusive(1, 100);
00509 // maybe want the managers to do this kind of stuff.
00510   if (do_what < 10) {
00511     // start a stocker.
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     // start a consumer.
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     // start a rearranger.
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 //hmmm: fix percentages when these threads exist.
00535   } else if (do_what < 31) {
00536     // start a defunctulater.
00537   } else if (do_what < 32) {
00538     // start an inventor.
00539   } else if (do_what < 33) {
00540     // start a manager.
00541   } else if (do_what < 50) {
00542     // start an inspector.
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     // fire someone.
00551     fire_an_employee();
00552   } else {
00553     // do nothing.
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     // there's no one else to fire currently.
00573     thread_bin->iter_unlock(iter);
00574     return;
00575   }
00576   // pick a random thread to zap.
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     // don't want to whack our own thread.
00589     thread_bin->iter_unlock(iter);
00590     return;
00591   }
00592   // lock the node for access.
00593   objcat_locket *l = iter->open_locket();
00594   ithread *to_zap = dynamic_cast<ithread *>(&l->cat());
00595   // tell the thread to get lost and wait until it does.
00596   to_zap->stop();
00597   unique_int whack_id = iter->cat()->id();
00598   iter->close_locket(l);
00599   thread_bin->iter_unlock(iter);
00600   // we've unlocked the list; now quash this id.
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   // time to get out of business.  cancel all the threads.
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     // loop through and tell all of the threads to stop.
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   // start over again.  this time we're removing stopped threads.
00623   iter->jump_head();
00624   for ( ; !iter->is_tail(); iter->next()) {
00625     // loop through and whack any stopped threads.
00626     if (!iter->cat()) continue;
00627     const ithread *t = dynamic_cast<const ithread *>
00628         (iter->cat());
00629     if (t->thread_finished()) {
00630       // this thread has stopped and should get whacked.
00631       thread_bin->zap(*iter);
00632       iter->previous();  // skip back to prior guy for next iteration.
00633     }
00634   }
00635   // start over once more.  now we're going to make sure we're not the last
00636   // one in the building.
00637   iter->jump_head();
00638   bool others_remain = false;
00639   for ( ; !iter->is_tail(); iter->next()) {
00640     // loop through and whack any stopped threads.
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     // we know we're the last one remaining now.
00649     cancel();  // close up shop.
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     // this is the time when we should get out of here.
00685 //hmmm: get time to run from command line.
00686 
00687   program_wide_logger().log("");
00688   program_wide_logger().log("");
00689   program_wide_logger().log("");  // some blank lines.
00690   LOG("starting test of object_catalog.");
00691 
00692   while (run_until > time_stamp()) { portable::sleep_ms(SHELL_SLEEP); }
00693 
00694   // wait until all the threads have exited.
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     // clean-up now while we have diagnostic facilities.
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 

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