1 /*****************************************************************************\
3 * Name : test_entity_data_bin_threaded *
4 * Author : Chris Koeritz *
6 *******************************************************************************
7 * Copyright (c) 2010-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
15 #include <application/hoople_main.h>
16 #include <basis/byte_array.h>
17 #include <mathematics/chaos.h>
18 #include <basis/functions.h>
19 #include <basis/guards.h>
20 #include <basis/astring.h>
21 #include <basis/mutex.h>
22 #include <structures/amorph.h>
23 #include <structures/static_memory_gremlin.h>
24 #include <loggers/console_logger.h>
25 #include <processes/ethread.h>
26 #include <processes/safe_roller.h>
27 #include <timely/time_control.h>
28 #include <timely/time_stamp.h>
29 #include <octopus/entity_data_bin.h>
30 #include <octopus/entity_defs.h>
31 #include <octopus/unhandled_request.h>
32 #include <application/application_shell.h>
33 #include <configuration/application_configuration.h>
34 #include <textual/string_manipulation.h>
42 using namespace application;
43 using namespace loggers;
44 using namespace octopi;
45 using namespace processes;
46 using namespace timely;
48 // global constants...
50 // how much data is the entity data bin allowed to hold at one time.
51 const int MAXIMUM_DATA_PER_ENTITY = 1 * KILOBYTE;
52 //tiny limit to test having too much data.
54 // controls the timing of the thread that adds items.
55 const int MIN_ADDER_THREAD_PAUSE = 3;
56 const int MAX_ADDER_THREAD_PAUSE = 20;
58 // controls the timing of the item deleting thread.
59 const int MIN_WHACKER_THREAD_PAUSE = 8;
60 const int MAX_WHACKER_THREAD_PAUSE = 70;
62 // bound the randomly chosen pause time for the cleanup thread.
63 const int MIN_TIDIER_THREAD_PAUSE = 60;
64 const int MAX_TIDIER_THREAD_PAUSE = 500;
66 // monk is kept asleep most of the time or he'd be trashing
67 // all our data too frequently.
68 const int MIN_MONK_THREAD_PAUSE = 2 * MINUTE_ms;
69 const int MAX_MONK_THREAD_PAUSE = 4 * MINUTE_ms;
71 // the range of new items added whenever the creator thread is hit.
72 const int MINIMUM_ITEMS_ADDED = 1;
73 const int MAXIMUM_ITEMS_ADDED = 20;
75 const int DEFAULT_THREADS = 90;
76 // the number of threads we create by default.
78 const int DEFAULT_RUN_TIME = 80 * MINUTE_ms;
80 // the length of time to run the program.
82 const int DATA_DECAY_TIME = 1 * MINUTE_ms;
83 // how long we retain unclaimed data.
85 const int MONKS_CLEANING_TIME = 10 * SECOND_ms;
86 // a very short duration for data to live.
88 #define LOG(to_print) printf("%s\n", (char *)astring(to_print).s());
89 //CLASS_EMERGENCY_LOG(program_wide_logger().get(), to_print)
90 // our macro for logging with a timestamp.
94 chaos _rando; // our randomizer.
96 // replace app_shell version with local randomizer, so all the static
97 // functions can employ it also.
98 #define randomizer() _rando
100 entity_data_bin binger(MAXIMUM_DATA_PER_ENTITY);
102 octopus_request_id create_request_id()
104 // test the basic filling of the values in an entity.
105 octopus_request_id req_id;
106 if (randomizer().inclusive(1, 100) < 25) {
107 // some of the time we make a totally random entity id.
108 int sequencer = randomizer().inclusive(1, MAXINT - 10);
109 int add_in = randomizer().inclusive(0, MAXINT - 10);
110 int process_id = randomizer().inclusive(0, MAXINT - 10);
111 req_id._entity = octopus_entity(string_manipulation::make_random_name(),
112 process_id, sequencer, add_in);
114 // sometimes we use a less random identity.
115 int sequencer = randomizer().inclusive(1, 3);
117 int process_id = randomizer().inclusive(1, 4);
118 req_id._entity = octopus_entity("boringentity",
119 process_id, sequencer, add_in);
121 req_id._request_num = randomizer().inclusive(1, MAXINT - 10);
125 // this thread creates new items for the entity data bin.
126 class ballot_box_stuffer : public ethread
129 ballot_box_stuffer() : ethread(0) {
130 FUNCDEF("constructor");
134 virtual ~ballot_box_stuffer() {
135 FUNCDEF("destructor");
139 DEFINE_CLASS_NAME("ballot_box_stuffer");
141 void perform_activity(void *formal(data)) {
142 FUNCDEF("perform_activity");
143 while (!should_stop()) {
144 // add a new item to the cache.
145 int how_many = randomizer().inclusive(MINIMUM_ITEMS_ADDED,
146 MAXIMUM_ITEMS_ADDED);
147 for (int i = 0; i < how_many; i++) {
148 string_array random_strings;
149 int string_count = randomizer().inclusive(1, 10);
150 // we create a random classifier, just to use up some space.
151 for (int q = 0; q < string_count; q++) {
152 random_strings += string_manipulation::make_random_name();
154 unhandled_request *newbert = new unhandled_request(create_request_id(),
156 binger.add_item(newbert, create_request_id());
159 int sleepy_time = randomizer().inclusive(MIN_ADDER_THREAD_PAUSE,
160 MAX_ADDER_THREAD_PAUSE);
161 time_control::sleep_ms(sleepy_time);
167 // this thread eliminates entries in the ballot box.
168 class vote_destroyer : public ethread
171 vote_destroyer() : ethread(0) {
172 FUNCDEF("constructor");
176 virtual ~vote_destroyer() {
177 FUNCDEF("destructor");
181 DEFINE_CLASS_NAME("vote_destroyer");
183 void perform_activity(void *formal(data)) {
184 FUNCDEF("perform_activity");
185 while (!should_stop()) {
186 // snag any old item and drop it on the floor.
187 octopus_request_id id;
188 infoton *found = binger.acquire_for_any(id);
191 int sleepy_time = randomizer().inclusive(MIN_WHACKER_THREAD_PAUSE,
192 MAX_WHACKER_THREAD_PAUSE);
193 time_control::sleep_ms(sleepy_time);
198 // this class makes sure the deadwood is cleaned out of the entity bin.
199 class obsessive_compulsive : public ethread
202 obsessive_compulsive() : ethread(0) {
203 FUNCDEF("constructor");
207 virtual ~obsessive_compulsive() {
208 FUNCDEF("destructor");
212 DEFINE_CLASS_NAME("obsessive_compulsive");
214 void perform_activity(void *formal(data)) {
215 FUNCDEF("perform_activity");
216 while (!should_stop()) {
217 // make sure there's nothing rotting too long.
218 binger.clean_out_deadwood(DATA_DECAY_TIME);
220 int sleepy_time = randomizer().inclusive(MIN_TIDIER_THREAD_PAUSE,
221 MAX_TIDIER_THREAD_PAUSE);
222 time_control::sleep_ms(sleepy_time);
227 // this thread will destroy all data in the bins while cleaning furiously.
228 class monk_the_detective : public ethread
231 monk_the_detective() : ethread(0) {
232 FUNCDEF("constructor");
236 virtual ~monk_the_detective() {
237 FUNCDEF("destructor");
241 DEFINE_CLASS_NAME("monk_the_detective");
243 void perform_activity(void *formal(data)) {
244 FUNCDEF("perform_activity");
245 while (!should_stop()) {
246 // one activation of monk has devastating consequences. we empty out
247 // the data one item at a time until we see no data at all. after
248 // cleaning each item, we ensure that the deadwood is cleaned out.
249 binger._ent_lock->lock();
250 LOG(a_sprintf("monk sees %d items.", binger.items_held()));
251 while (binger.items_held()) {
252 // grab one instance of any item in the bin.
253 octopus_request_id id;
254 infoton *found = binger.acquire_for_any(id);
256 // also clean out things a lot faster than normal.
257 binger.clean_out_deadwood(MONKS_CLEANING_TIME);
259 binger._ent_lock->unlock();
260 LOG(a_sprintf("after a little cleaning, monk sees %d items.", binger.items_held()));
262 int sleepy_time = randomizer().inclusive(MIN_MONK_THREAD_PAUSE,
263 MAX_MONK_THREAD_PAUSE);
264 time_control::sleep_ms(sleepy_time);
271 class test_entity_data_bin_threaded : public application_shell
274 test_entity_data_bin_threaded() : application_shell(class_name()) {}
276 DEFINE_CLASS_NAME("test_entity_data_bin_threaded");
281 int test_entity_data_bin_threaded::execute()
285 amorph<ethread> thread_list;
287 for (int i = 0; i < DEFAULT_THREADS; i++) {
288 ethread *t = NULL_POINTER;
289 if (i == DEFAULT_THREADS - 1) {
290 // last item gets special treatment; we reserve this space for monk.
291 t = new monk_the_detective;
292 } else if (i % 3 == 0) {
293 t = new ballot_box_stuffer;
294 } else if (i % 3 == 1) {
295 t = new vote_destroyer;
296 } else { // i % 3 must = 2.
297 t = new obsessive_compulsive;
299 thread_list.append(t);
300 ethread *q = thread_list[thread_list.elements() - 1];
302 deadly_error(class_name(), func, "amorph has incorrect pointer!");
303 // start the thread we added.
304 thread_list[thread_list.elements() - 1]->start(NULL_POINTER);
307 time_stamp when_to_leave(DEFAULT_RUN_TIME);
308 while (when_to_leave > time_stamp()) {
309 time_control::sleep_ms(100);
312 // LOG("now cancelling all threads....");
314 // for (int j = 0; j < thread_list.elements(); j++) thread_list[j]->cancel();
316 // LOG("now stopping all threads....");
318 // for (int k = 0; k < thread_list.elements(); k++) thread_list[k]->stop();
320 // LOG("resetting thread list....");
322 thread_list.reset(); // should whack all threads.
324 LOG("done exiting from all threads....");
326 //report the results:
327 // how many objects created.
328 // how many got destroyed.
329 // how many evaporated due to timeout.
332 guards::alert_message("t_bin_threaded:: works for all functions tested.");
336 HOOPLE_MAIN(test_entity_data_bin_threaded, )