Lumiera  0.pre.03
»edityourfreedom«
call-queue-test.cpp
Go to the documentation of this file.
1 /*
2  CallQueue(Test) - verify queue based dispatch of bound function objects
3 
4  Copyright (C) Lumiera.org
5  2017, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
28 #include "lib/test/run.hpp"
31 #include "lib/sync.hpp"
32 #include "lib/util.hpp"
33 
34 #include "lib/call-queue.hpp"
35 
36 #include <functional>
37 #include <string>
38 
39 
40 
41 namespace lib {
42 namespace test{
43 
44  using lib::Sync;
46 
47  using util::isnil;
48  using std::string;
49  using std::bind;
50 
51 
52 
53  namespace { // test fixture
54 
55  // --------random-stress-test------
56  uint const NUM_OF_THREADS = 50;
57  uint const MAX_RAND_INCMT = 200;
58  uint const MAX_RAND_STEPS = 500;
59  uint const MAX_RAND_DELAY = 1000;
60  // --------random-stress-test------
61 
62 
63  uint calc_sum = 0;
64  uint ctor_sum = 0;
65  uint dtor_sum = 0;
66 
67  template<uint i>
68  struct Dummy
69  {
70  uint val_;
71 
72  Dummy()
73  : val_(i)
74  {
75  ctor_sum += (val_+1);
76  }
77 
78  ~Dummy()
79  {
80  dtor_sum += val_;
81  }
82 
83  int
84  operator++()
85  {
86  return ++val_;
87  }
88  };
89 
90  template<uint i>
91  void
92  increment (Dummy<i>&& dummy) //NOTE: dummy is consumed here
93  {
94  calc_sum += ++dummy;
95  }
96 
97  }//(End) test fixture
98 
99 
100 
101 
102  /**********************************************************************************/
112  class CallQueue_test : public Test
113  {
114 
115  virtual void
117  {
118  verify_SimpleUse();
119  verify_Consistency();
120  verify_ThreadSafety();
121  }
122 
123 
124  void
126  {
127  CallQueue queue;
128  CHECK (isnil (queue));
129 
130  int val = 2;
131  queue.feed ([&]() { val = -1; });
132  CHECK (1 == queue.size());
133  CHECK (val == 2);
134 
135  queue.invoke();
136  CHECK (val == -1);
137  CHECK (0 == queue.size());
138 
139  queue.invoke();
140  CHECK (0 == queue.size());
141  }
142 
143 
154  void
156  {
157  calc_sum = 0;
158  ctor_sum = 0;
159  dtor_sum = 0;
160 
161  CallQueue queue;
162  queue.feed ([]() { increment(Dummy<0>{}); }); //NOTE: each lambda binds a different instantiation of the increment template
163  queue.feed ([]() { increment(Dummy<1>{}); }); // and each invocation closes over an anonymous rvalue instance
164  queue.feed ([]() { increment(Dummy<2>{}); });
165 
166  queue.invoke();
167  queue.invoke();
168  queue.feed ([]() { increment(Dummy<3>{}); });
169  queue.feed ([]() { increment(Dummy<4>{}); });
170 
171  queue.invoke();
172  queue.invoke();
173  queue.invoke();
174 
175  uint expected = (5+1)*5/2;
176  CHECK (calc_sum = expected);
177  CHECK (ctor_sum = expected);
178  CHECK (dtor_sum = expected);
179  }
180 
181 
182 
183  struct Worker
185  , Sync<>
186  {
187  uint64_t producerSum = 0;
188  uint64_t consumerSum = 0;
189 
190  void
191  countConsumerCall (uint increment)
192  {
193  Lock sync(this); // NOTE: will be invoked from some random other thread
194  consumerSum += increment;
195  }
196 
198  : ThreadJoinable{"CallQueue_test: concurrent dispatch"
199  , [&]() {
200  uint cnt = rand() % MAX_RAND_STEPS;
201  uint delay = rand() % MAX_RAND_DELAY;
202 
203  syncPoint(); // block until all threads are ready
204  for (uint i=0; i<cnt; ++i)
205  {
206  uint increment = rand() % MAX_RAND_INCMT;
207  queue.feed ([=]() { countConsumerCall(increment); });
208  producerSum += increment;
209  usleep (delay);
210  queue.invoke(); // NOTE: dequeue one functor added during our sleep
211  } // and thus belonging to some random other thread
212  }}
213  { }
214  };
215 
217 
218 
225  void
227  {
228  CallQueue queue;
229 
230  // Start a bunch of threads with random access pattern
231  Workers workers{NUM_OF_THREADS,
232  [&](Workers::ElementHolder& storage)
233  {
234  storage.create<Worker>(queue);
235  }
236  };
237 
238  // unleash all worker functions
239  for (auto& thread : workers)
240  thread.sync();
241 
242  // wait for termination of all threads
243  for (auto& worker : workers)
244  worker.join();
245 
246  // collect the results of all worker threads
247  uint64_t globalProducerSum = 0;
248  uint64_t globalConsumerSum = 0;
249  for (auto& worker : workers)
250  {
251  globalProducerSum += worker.producerSum;
252  globalConsumerSum += worker.consumerSum;
253  }
254 
255  // VERIFY: locally recorded partial sums match total sum
256  CHECK (globalProducerSum == globalConsumerSum);
257  }
258  };
259 
260 
262  LAUNCHER (CallQueue_test, "unit common");
263 
264 
265 }} // namespace lib::test
Facility for monitor object based locking.
Definition: sync.hpp:425
CallQueue & invoke()
Definition: call-queue.hpp:86
A fixed collection of non-copyable polymorphic objects.
A threadsafe queue for bound void(void) functors.
Definition: call-queue.hpp:58
Definition: run.hpp:49
CallQueue & feed(Operation &&op)
Definition: call-queue.hpp:73
scoped object to control the actual locking.
Definition: sync.hpp:442
Implementation namespace for support and library code.
Managing a collection of non-copyable polymorphic objects in compact storage.
Storage Frame to hold one Child object.
Object Monitor based synchronisation.
unsigned int delay[threads_per_pool_count *LUMIERA_THREADCLASS_COUNT]
A Queue for function invocations, allowing them to be dispatched on demand.
std::vector< string > & Arg
Definition: run.hpp:54
Simple test class runner.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Convenience front-end for basic thread handling needs.
size_t size() const
Definition: call-queue.hpp:106
bool isnil(lib::time::Duration const &dur)
Definition: timevalue.hpp:642
LAUNCHER(SyncClasslock_test, "unit common")
Register this test class...
Variant of the standard case, allowing additionally to join on the termination of this thread...
void countConsumerCall(uint increment)
virtual ~Dummy()
Definition: testdummy.hpp:59
void sync()
Synchronisation barrier.