threadsafe/test/test1.cpp

Go to the documentation of this file.
00001 /*
00002  * test1.cpp
00003  * 
00004  * Copyright (C) 2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Testing for threadsafe collections
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include <iostream>
00036 
00037 #include "common/wave_ex.h"
00038 #include "perf/perf.h"
00039 #include "threadsafe/smart_ptr.h"
00040 #include "threadsafe/threadsafe_counter.h"
00041 #include "threadsafe/threadsafe_map.h"
00042 #include "threadsafe/threadsafe_multimap.h"
00043 #include "threadsafe/threadsafe_queue.h"
00044 
00045 #ifdef WIN32
00046 #define sleep(x) Sleep(1000 * x)
00047 #endif  // WIN32
00048 
00049 typedef threadsafe_multimap<std::string, std::string> multi_string_t;
00050 typedef threadsafe_queue<std::string> tqueue_string_t;
00051 
00052 
00053 class ATest {
00054 public:
00055         ATest(void) throw() { }
00056         virtual ~ATest(void) throw() { }
00057 
00058 private:
00059         long            m_l;
00060 };
00061 
00062 
00063 class BTest : public ATest {
00064 public:
00065         ~BTest(void) throw() { }
00066 
00067 private:
00068         long            m_l2;
00069 };
00070 
00071 
00072 typedef smart_ptr<ATest> ptr_t;
00073 
00074 typedef void * (*get_context_fn)(void);
00075 typedef void (*test_routine_t)(void *);
00076 typedef void (*valid_fn)(void *);
00077 
00078 struct context_t {
00079         context_t(void) throw() {
00080                         ctr = NULL;
00081                         fn = NULL;
00082                         ctx = NULL;
00083                 }
00084 
00085         // data fields
00086         threadsafe_counter *    ctr;    // counter
00087         test_routine_t          fn;     // function to execute
00088         void *                  ctx;    // context
00089 };
00090 
00091 
00092 ////////////////////////////////////////////////////////////////////////////////
00093 //
00094 //      static helper methods
00095 //
00096 ////////////////////////////////////////////////////////////////////////////////
00097 
00098 static float
00099 getRandom
00100 (
00101 void
00102 )
00103 throw()
00104 {
00105         return (1.0 * rand()) / RAND_MAX;
00106 }
00107 
00108 
00109 
00110 static void *
00111 getContext1
00112 (
00113 void
00114 )
00115 {
00116         tqueue_string_t * tqs = new tqueue_string_t;
00117         ASSERT(tqs, "out of memory");
00118 
00119         return tqs;
00120 }
00121 
00122 
00123 
00124 static void *
00125 getSPtrContext
00126 (
00127 void
00128 )
00129 {
00130         ptr_t * p = new ptr_t;
00131         ASSERT(p, "out of memory");
00132         *p = new BTest;
00133         ASSERT(*p, "out of memory");
00134         ASSERT(1 == p->get_ref_count(), "Bad ref count? %ld",
00135             p->get_ref_count());
00136 
00137         return (void *) p;
00138 }
00139 
00140 
00141 
00142 static void *
00143 getMMctx
00144 (
00145 void
00146 )
00147 {
00148         multi_string_t * ps = new multi_string_t;
00149         ASSERT(ps, "out of memory");
00150         return ps;
00151 }
00152 
00153 
00154 
00155 static void
00156 doTest1
00157 (
00158 IN void * pv
00159 )
00160 {
00161         
00162         tqueue_string_t * tqs = (tqueue_string_t *) pv;
00163         ASSERT(tqs, "null context");
00164 
00165         std::string val;
00166         float x = getRandom();
00167         if (x < 0.3) {
00168                 //DPRINTF("pushing...");
00169                 val = "test";
00170                 tqs->push_back(val);
00171         } else if (x < 0.7) {
00172                 //DPRINTF("popping...");
00173                 tqs->pop_front(val);
00174         } else {
00175                 //DPRINTF("iterating...");
00176                 tqueue_string_t::iterator_t i;
00177                 tqs->getIterator(i);
00178                 while (tqs->getNextElement(i, val)) {
00179                 }
00180         }
00181 }
00182 
00183 
00184 
00185 static void
00186 doSPtrTest
00187 (
00188 IN void * pv
00189 )
00190 {
00191         ptr_t * p = (ptr_t *) pv;
00192         ASSERT(p, "null context");
00193         ASSERT(p->get_ref_count() > 0, "Bad ref count!");
00194 
00195         ptr_t newP(*p);
00196         ASSERT(newP, "should be non-null!");
00197         ASSERT(newP.get_ref_count() > 1, "should be at least 2! %ld",
00198             newP.get_ref_count());
00199 }
00200 
00201 
00202 
00203 static void
00204 doMultiTest
00205 (
00206 IN void * pv
00207 )
00208 {
00209         multi_string_t * ps = (multi_string_t *) pv;
00210         ASSERT(ps, "null context");
00211 
00212         // iterate over all keys
00213         multi_string_t::iterator_t i;
00214         ps->getIterator(i);
00215         std::string key;
00216         std::string value;
00217         while (ps->getNextElement(i, key, value)) {
00218 //              DPRINTF("  '%s' --> '%s'", key.c_str(), value.c_str());
00219         }
00220 
00221         // iterate over all 'a' keys
00222 //      DPRINTF("Have %d elements with key 'a'", ps->count("a"));
00223         ps->getIterator("a", i);
00224         while (ps->getNextElement(i, key, value)) {
00225 //              DPRINTF("  '%s' --> '%s'", key.c_str(), value.c_str());
00226         }
00227 
00228         // add some keys
00229         int nAdd = rand() % 10;
00230         char buffer[2];
00231         buffer[1] = 0;
00232         for (int j = 0; j < nAdd; ++j) {
00233                 buffer[0] = 'a' + (rand() % 26);
00234                 ps->insert(buffer, buffer);
00235         }
00236 
00237         // delete some keys
00238         buffer[0] = 'a' + (rand() % 26);
00239         ps->remove(buffer);
00240 }
00241 
00242 
00243 
00244 static void
00245 validateSPtr
00246 (
00247 IN void * pv
00248 )
00249 {
00250         ptr_t * p = (ptr_t *) pv;
00251         ASSERT(p, "null context?");
00252 
00253         if (1 != p->get_ref_count()) {
00254                 WAVE_EX(wex);
00255                 wex << "smart pointer test failed ref count!\n";
00256                 wex << "Expected exactly 1 ref count, but found ";
00257                 wex << p->get_ref_count();
00258         }
00259         delete p;
00260 }
00261 
00262 
00263 
00264 static void *
00265 threadStartWrapper
00266 (
00267 IN void * pv
00268 )
00269 {
00270         context_t * ctx = (context_t *) pv;
00271         ASSERT(ctx, "null context");
00272         ASSERT(ctx->ctr, "null counter");
00273         ASSERT(ctx->fn, "null test function");
00274 
00275         // auto increment/decrement counter
00276         counter_ref cref(ctx->ctr);
00277 
00278         // now loop many times and call test function
00279         for (int i = 0; i < 100 * 1000; ++i) {
00280                 sleep(0);
00281                 ctx->fn(ctx->ctx);
00282         }
00283 
00284         // all done!
00285         return NULL;
00286 }
00287 
00288 
00289 
00290 
00291 void
00292 threadLoop
00293 (
00294 IN int nThreads,
00295 IN get_context_fn getContext,
00296 IN test_routine_t fn,
00297 IN valid_fn valid = NULL
00298 )
00299 {
00300         ASSERT(nThreads > 0, "Bad thread count: %d", nThreads);
00301         ASSERT(fn, "null thread start function");
00302         // ASSERT(valid) -- can be null!
00303 
00304         threadsafe_counter c;
00305 
00306         context_t ctx;
00307         ctx.ctx = getContext();
00308         ctx.ctr = &c;
00309         ctx.fn = fn;
00310 
00311         for (int i = 0; i < nThreads; ++i) {
00312                 pthread_t id;
00313                 pthread_create(&id, NULL, threadStartWrapper, &ctx);
00314         }
00315 
00316         // wait for threads to exit
00317         sleep(1);
00318         while (c.getCount()) {
00319                 sleep(1);
00320         }
00321         DPRINTF("Done waiting for threads!");
00322 
00323         if (valid) {
00324                 valid(ctx.ctx);
00325         }
00326 }
00327 
00328 
00329 
00330 ////////////////////////////////////////////////////////////////////////////////
00331 //
00332 //      entry point
00333 //
00334 ////////////////////////////////////////////////////////////////////////////////
00335 
00336 int
00337 main
00338 (
00339 IN int argc,
00340 IN const char * argv[]
00341 )
00342 {
00343         int retval = 0;
00344 
00345         int nThreads = 10;
00346         if (argc > 1) {
00347                 nThreads = atoi(argv[1]);
00348         }
00349         DPRINTF("Using %d threads.", nThreads);
00350         ASSERT(nThreads > 0, "Bad thread count: %d", nThreads);
00351 
00352         try {
00353                 perf::Timer timer("threadsafe test1 -- overall timer");
00354 
00355                 // verify iterators
00356                 int i1 = sizeof(tqueue_string_t::iterator_t);
00357                 int i2 = tqueue_string_t::getRealIteratorSize();
00358                 DPRINTF("Public queue iterator size:  %d bytes", i1);
00359                 DPRINTF("Private queue iterator size: %d bytes", i2);
00360                 if (i2 > i1) {
00361                         WAVE_EX(wex);
00362                         wex << "Queue iterator size problem!";
00363                 }
00364 
00365                 DPRINTF("Testing smart_ptr<T>...");
00366                 threadLoop(nThreads, getSPtrContext, doSPtrTest, validateSPtr);
00367 
00368                 DPRINTF("Testing queues...");
00369                 threadLoop(nThreads, getContext1, doTest1);
00370 
00371                 DPRINTF("Testing multimap...");
00372                 threadLoop(nThreads, getMMctx, doMultiTest);
00373 
00374         } catch (std::exception& e) {
00375                 DPRINTF("Exception: %s", e.what());
00376                 retval = 1;
00377         }
00378 
00379         perf::dumpTimingSummary(std::cerr);
00380 
00381         // all done
00382         return retval;
00383 }
00384