Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Array_MT_UnitTests_Decl.hpp
Go to the documentation of this file.
1 /*
2 // @HEADER
3 // ***********************************************************************
4 //
5 // Teuchos: Common Tools Package
6 // Copyright (2004) Sandia Corporation
7 //
8 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
9 // license for use of this work by or on behalf of the U.S. Government.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
39 //
40 // ***********************************************************************
41 // @HEADER
42 */
43 
44 // These unit tests are used for both a Nightly version and a Basic version
45 
46 #include "General_MT_UnitTests.hpp"
47 
48 #include "Teuchos_Array.hpp"
49 #include "Teuchos_RCP.hpp"
52 #include <vector>
53 #include <thread>
54 
55 namespace {
56 
57 using Teuchos::Array;
58 using Teuchos::RCP;
61 
62 // this thread method repeatedly calls the iterator loop and reads values
63 // arrayType will be Array or const Array
64 // iteratorType will be corresponding ::iterator or ::const_iterator
65 template<typename arrayType, typename iteratorType>
66 static void thread_reads_array(arrayType shared_array, int setValue) {
67  while (!ThreadTestManager::s_bAllowThreadsToRun) {}
68  for( int n = 0; n < 1000; ++n) {
69  for (iteratorType iter = shared_array->begin();
70  iter < shared_array->end(); ++iter) {
71  int readValue = *iter; // read the value
72  if(readValue != setValue) {
73  throw std::logic_error("Test failed to read proper array value.");
74  }
75  }
76  }
77 }
78 
79 // Shared method for Array and const Array unit tests
80 // See notes below for individual unit tests
81 template<typename arrayType, typename iteratorType>
82 void runReadArrayTest()
83 {
84  const int numThreads = TEUCHOS_THREAD_SAFE_UNIT_TESTS_THREADS_USED;
85  const int numTests = NUM_TESTS_TO_RUN;
86  const int setArrayLength = 10; // somewhat arbitrary
87  const int setArrayValue = 3; // arbitrary
88  for (int testCycle = 0; testCycle < numTests; ++testCycle) {
89  std::vector<std::thread> threads;
90  // set up threads to be spin locked
91  ThreadTestManager::s_bAllowThreadsToRun = false;
92  // makes an array of length setArrayLength with each
93  // element set to setArrayValue
94  arrayType array_rcp =
95  rcp(new Array<int>( setArrayLength, setArrayValue ));
96  // create multiple threads which read the array
97  for (int i = 0; i < numThreads; ++i) {
98  threads.push_back(std::thread(
99  thread_reads_array<arrayType, iteratorType>,
100  array_rcp, setArrayValue));
101  }
102  // let the threads run
103  ThreadTestManager::s_bAllowThreadsToRun = true;
104  // join the threads
105  for (unsigned int i = 0; i < threads.size(); ++i) {
106  threads[i].join();
107  }
108  convenience_log_progress(testCycle, numTests); // just output
109  }
110 }
111 
112 // RCP Thread Safety Unit Test: mtArrayMultipleReads_NonConst
113 //
114 // Purpose:
115 // Demonstrates the Array class needs thread safety protection for debug mode
116 // since the debug ArrayRCP objects are allocated in the begin() function.
117 //
118 // Description:
119 // An Array is shared to multiple threads - they simultaneously use the
120 // non-const iterator - so this calls begin(), end(), and ++iter on the
121 // thread which was sufficient to demonstrate the original problem.
122 // Later added reads of the iterator for more complete testing.
123 // The point of this test was to validate that multiple threads can safely
124 // read a shared Array. We expected it to fail originally because the begin()
125 // call in Debug will set extern_arcp_ so the first strategy was to make a
126 // race condition on that allocation to demonstrate we could see this problem.
127 // Note that begin() is a const but the internal extern_arcp_ object is
128 // mutable - that object could be changed by multiple threads simultaneously.
129 //
130 // Solution to the Problem:
131 // A single debug-only mutex protects all changes to the member vec_ as well
132 // as the debug ArrayRCP objects to avoid any simultaneous read/write
133 // behavior.
134 //
135 // Demonstration of Original Problem:
136 // Add the following define at top of this file:
137 // #define REMOVE_THREAD_PROTECTION_FOR_ARRAY
138 // That will remove the mutex protecting the debug mode.
139 TEUCHOS_UNIT_TEST( Array, mtArrayMultipleReads_NonConst )
140 {
141  try {
142  runReadArrayTest<RCP<Array<int>>, Array<int>::iterator>();
143  }
144  TEUCHOS_STANDARD_CATCH_STATEMENTS(true, std::cerr, success);
145 }
146 
147 // RCP Thread Safety Unit Test: mtArrayMultipleReads_Const
148 //
149 // Purpose:
150 // Similar to mtArrayMultipleReads_NonConst.
151 //
152 // Description:
153 // Similar to mtArrayMultipleReads_NonConst.
154 //
155 // Solution to the Problem:
156 // Similar to mtArrayMultipleReads_NonConst though the original Array form had
157 // the const begin() call the non-const begin(), so this was refactored to
158 // facilitate the mutex protection
159 //
160 // Demonstration of Original Problem:
161 // Similar to mtArrayMultipleReads_NonConst.
162 TEUCHOS_UNIT_TEST( Array, mtArrayMultipleReads_Const )
163 {
164  try {
165  runReadArrayTest<const RCP<Array<int>>, Array<int>::const_iterator>();
166  }
167  TEUCHOS_STANDARD_CATCH_STATEMENTS(true, std::cerr, success);
168 }
169 
170 // These tests are only meaningful in DEBUG
171 // They would crash with undefined behaviors in RELEASE
172 // All of this Array work was designed to make the debug tracking for Array
173 // work in a thread safe way but in release those protections don't exist.
174 #ifdef TEUCHOS_DEBUG
175 
176 // This method runs continuously inserting setValue on shared_array and then
177 // removing those values. The shared_array never gets larger than maxArraySize
178 // The process completes when finishWhenThisThreadCountCompletes determines
179 // that all the reading threads have completed their operations.
180 // For the unit tests there will only be 1 thread calling this method.
181 static void call_inserts_on_array(RCP<Array<int>> shared_array, int setValue,
182  int maxArraySize, int finishWhenThisThreadCountCompletes) {
183  while (!ThreadTestManager::s_bAllowThreadsToRun) {}
184  while (true) { // runs until read threads register completion
185  const int insertCount = maxArraySize - shared_array->size();
186  // insert some values
187  for (int n = 0; n < insertCount; ++n) {
188  shared_array->push_back(setValue); // insert ints with value setValue
189  }
190  // erase some values
191  for (int n = 0; n < insertCount; ++n) {
192  shared_array->pop_back(); // remove values so it doesn't get too big
193  }
194  if (ThreadTestManager::s_countCompletedThreads >=
195  finishWhenThisThreadCountCompletes) {
196  break;
197  }
198  // track cycles - used to make sure this thread is up and running before
199  // we begin testing the read threads
200  ++ThreadTestManager::s_countWritingThreadCycles;
201  }
202 }
203 
204 // This method runs continuously deleting and allocated memory.
205 // When Weak RCP checks for a valid ptr and another strong RCP deallocates,
206 // the memory may be lost after the check but before the actual read.
207 // This scramble thread can claim that memory, and replace the deallocated
208 // memory spot with a new scramble value. This allows the unit test to detect
209 // that this bad memory event has occurred. Note that currently we do not
210 // have a fix for this Debug Weak RCP check
211 static void scramble_memory(int scrambleValue,
212  int finishWhenThisThreadCountCompletes) {
213  while (!ThreadTestManager::s_bAllowThreadsToRun) {}
214  while (true) {
215  // here I used a strict C array to avoid having this code interfering with
216  // any debugging of the actual Array class
217  // Allocates 100 int ptrs, sets them, then deletes the memory, then repeat
218  // Better than just new-delete repeated because we don't want to just
219  // attack the same memory slot over and over.
220  #define ARRAY_SCRAMBLE_SIZE 100
221  int * tempPtrArray[ARRAY_SCRAMBLE_SIZE];
222  for (int n = 0; n < ARRAY_SCRAMBLE_SIZE; ++n) {
223  int * pInt = new int;
224  *pInt = scrambleValue;
225  tempPtrArray[n] = pInt;
226  }
227  for (int n = 0; n < ARRAY_SCRAMBLE_SIZE; ++n) {
228  delete tempPtrArray[n];
229  }
230  if (ThreadTestManager::s_countCompletedThreads >=
231  finishWhenThisThreadCountCompletes) {
232  break;
233  }
234  }
235 }
236 
237 // defined three modes of array testing
238 // during these tests one thread will be inserting/removing values and
239 // another thread will be allocating/deleting memory.
240 // All other threads will perform the operation defined here.
241 enum ArrayTest_Style {
242  // Will try to read array - this currently can lead to out of range errors
243  // no iteration occurs in thie mode.
244  ArrayTest_DoReadOperations,
245 
246  // Iterator will trigger dangling references but this simple form may
247  // not trigger dangling the first cycle just due to random luck of the draw.
248  ArrayTest_TriggerDanglingWithIteration,
249 
250  // Modified test to hit dangling reference immediately on the first cycle.
251  // It's a sanity check and the test happens in a deterministic way.
252  ArrayTest_TriggerDanglingWithIterationFirstCycle
253 };
254 
255 // This method will be called on 2 threads and run an operation
256 // defined by arrayTestStyle. While those 2 threads run there will also be
257 // 2 special threads running - one is inserting/removing values while another
258 // is constantly allocating/deleting memory.
259 template<class arrayType>
260 static void do_read_operations_on_array(RCP<arrayType> shared_array,
261  int setValue, int scrambleValue, Cycle_Index_Tracker & index_tracker,
262  int maxArraySize, ArrayTest_Style arrayTestStyle) {
263  // spin lock until the threads are released
264  while (!ThreadTestManager::s_bAllowThreadsToRun) {}
265  int cycle = 0; // track the loop cycle so we can save it
266  try {
267  // once we catch the event this loop ends so the count should not impact
268  // the time for the test. 10000 is just to have a safety exit and then the
269  // test can fail due to not detecting the events.
270  for (cycle = 0; cycle < 10000; ++cycle) {
271  switch (arrayTestStyle) {
272  case ArrayTest_DoReadOperations:
273  {
274  // read some values and check for scrambles. Two things can happen,
275  // either we get a range error and get a valid exception or perhaps
276  // we just straight out read bad memory in which case we won't get
277  // an exception but will try to trap that event here and record it.
278  int readValue = shared_array->at(0);
279  if( readValue != setValue ) {
280  // was using this to see if things were working properly - but we
281  // don't necessarily expect the scrambled int to always measure
282  // here - something else could be going on.
283  if( readValue == scrambleValue) {
284  // we detected a bad memory read and the int was set by the
285  // scrambling thread. That confirms we really did read memory
286  // which was allocated by another thread
287  index_tracker.scambledMemory = cycle;
288  }
289  else {
290  // we detected bad memory but it was not set by the scrambling
291  // thread. Anything else could be going on to have changed this.
292  // For now just do the same as above and mark we detected the
293  // error. We are not recording the distinction between this and
294  // the above case right now. I have left both entries for debug
295  // testing.
296  index_tracker.scambledMemory = cycle;
297  }
298  }
299  }
300  break;
301  case ArrayTest_TriggerDanglingWithIteration:
302  {
303  // this is perhaps the most 'natural' way to test the danglers
304  // detection using iterators. However note that this is not
305  // guaranteed to trigger a dangling reference on a particular cycle.
306  // shared_array()->begin() and shared_array->end() do not depend on
307  // the iter arrayRCP so they won't necessarily pick up the dangling
308  // reference immediately. It's possible for shared_array to be
309  // changing in a state so that iter++ never gets called after it
310  // gets set to shared_array->begin(). For example
311  // shared_array()->end() may change to be much smaller than begin(),
312  // or much larger(). Then the loop ends and we go to the next cycle.
313  // That is why this test loops in the first place.
314  // Another test bGuaranteeDanglingDetectionOnFirstCycle true is
315  // designed to always hit dangling on the first cycle for clarity
316  // and to provide a deterministic test mode.
317  for (Array<int>::const_iterator iter = shared_array->begin();
318  iter < shared_array->end(); ++iter) {
319  // empty loop because we are testing the iterators and how they
320  // behave when another thread changes the array in the middle of
321  // iterating.
322  }
323  }
324  break;
325  case ArrayTest_TriggerDanglingWithIterationFirstCycle:
326  {
327  // this deterministic mode is designed to hit the dangling reference
328  // on the very first cycle. If we got to the second cycle we didn't
329  // succeed and need to report an error.
330  if (cycle != 0) {
331  // log the event so the test can fail
332  index_tracker.missedDanglingOnFirstCycle = cycle;
333 
334  // inform other threads now that this thread is completed
335  // so they can quit properly
336  ++ThreadTestManager::s_countCompletedThreads;
337 
338  // this will exit the thread loop immediately
339  return;
340  }
341 
342  // This is the logic to make this threead always hit the dangling
343  // reference on the first cycle. First we allocate an iterator.
344  Array<int>::const_iterator iter = shared_array->begin();
345 
346  // determine what thread cycle the read/write thread is currently on
347  int getReadWriteThreadCycles =
348  ThreadTestManager::s_countWritingThreadCycles;
349 
350  // spin lock this thread until the read/write thread does at
351  // least one full cycle beyond where it was 'after' the moment
352  // we created the iterator.
353  while (ThreadTestManager::s_countWritingThreadCycles <
354  getReadWriteThreadCycles + 2) { // empty loop
355  }
356 
357  // Now call ++iter once - this MUST fail and detect the dangler
358  // In this condition iter is always a weak reference to a node with
359  // an invalid ptr.
360  ++iter; // the test must throw here!
361  }
362  break;
363  }
364  }
365  }
366  catch (DanglingReferenceError) {
367  // If test throws a dangling reference error, record the cycle it occurred.
368  index_tracker.danglingReference = cycle;
369  }
370  catch (RangeError) {
371  // If tests throws a range error, record the cycle it occurred.
372  index_tracker.outOfRangeError = cycle;
373  }
374  catch (std::out_of_range) {
375  // We could also get std::out_of_range
376  // Note that here were are counting Trilinos RangeError and STL
377  // std::out_of_range as all the same - they both happen since the bad
378  // memory read can be triggered at any time and it depends on exactly where
379  // we were in the call stack when things happened.
380  index_tracker.outOfRangeError = cycle;
381  }
382 
383  // advertise we are done. When all these threads complete,
384  // the push/pop thread and scambler thread will quit as well.
385  ++ThreadTestManager::s_countCompletedThreads;
386 }
387 
388 // This method will set up:
389 // One thread which is constantly inserting/deleting values form shared_array.
390 // Another thread will constantly allocate/delete
391 // Remaining threads will all run do_read_operations_on_array (above)
392 // with a read operation defined by arrayTestStyle.
393 // This method is used for all the unit tests except for the two simple
394 // read tests defined above.
395 bool runArrayDanglingReferenceTest( bool bUseConstVersion,
396  ArrayTest_Style arrayTestStyle ) {
397  // Note that 0 is the insert thread, and 1 is the scrambler thread so set
398  // this to be 4 or more - 3 is not ideal as only have one thread reading.
399  // Using 4 because NUM_TOTAL_CORES_USED - 4
400  const int numThreads = TEUCHOS_THREAD_SAFE_UNIT_TESTS_THREADS_USED;
401 
402  // how many times to repeat the entire test
403  const int numTests = NUM_TESTS_TO_RUN;
404 
405  // arbitrary - set up an array filled with this value
406  const int setValue = 1;
407 
408  // arbitrary - but not the same as setValue
409  // the scramble thread tries to allocate memory during the race condition
410  // which occurs for weak RCP when it checks for valid memory and then loses
411  // that memory before actually reading it. By setting a known value the unit
412  // test can confirm it is registering that event. Exactly how we will
413  // resolve that is a pending issue.
414  const int scrambleValue = 12345;
415 
416  // The insert/delete thread will build the array up to this size and
417  // then start to delete elements
418  const int maxArraySize = 100;
419 
420  // the maximum number of errors we can detect.
421  // One per thread which is doing read operations.
422  // Does not count the push/pop thread or the scramble thread.
423  int countTotalTestRuns = 0;
424 
425  // the actual number of dangling references we detect
426  int countDetectedDanglingReferences = 0;
427 
428  // if using the test designed to hit first cycle, this tracks failures
429  int countMissedFirstCycleDanglers = 0;
430 
431  // this counts the times the dangling reference missed (thought memory was
432  // fine) but it was actually not and it was found to be overwritten to the
433  // scrambleValue
434  int countScrambledMemoryEvents = 0;
435 
436  // this counts out of range errors which are currently triggered by reading
437  // the array with a concurrent write - currently Trilinos RangeError and
438  // STL std::out_of_range are grouped together
439  int countOutOfRangeEvents = 0;
440 
441  for (int testCycle = 0; testCycle < numTests; ++testCycle) {
442  std::vector<std::thread> threads;
443  ThreadTestManager::s_bAllowThreadsToRun = false;
444  ThreadTestManager::s_countCompletedThreads = 0;
445  ThreadTestManager::s_countWritingThreadCycles = 0;
446 
447  // 0 is pushing/popping, 1 is memory reading/writing.
448  // The rest are the reader threads looking for troubles
449  int finishWhenThisThreadCountCompletes = numThreads - 2;
450 
451  // I avoid using general Arrays as we are testing them,
452  // makes debugging thread issues easier
453  Cycle_Index_Tracker index_tracker[numThreads];
454 
455  // makes an array of length 1 - so we will cycle from size 1 to
456  // size maxArraySize then back to 1
457  RCP<Array<int>> array_rcp = rcp(new Array<int>(1, setValue));
458 
459  for (int i = 0; i < numThreads; ++i) {
460  switch (i)
461  {
462  case 0:
463  // create the insert/delete thread which constantly changes the
464  // size of the array
465  threads.push_back( std::thread(call_inserts_on_array, array_rcp,
466  setValue, maxArraySize, finishWhenThisThreadCountCompletes) );
467  break;
468  case 1:
469  // create the srambler thread which puts pressure on memory
470  // with constant allocations and deletes. This allows us to detect
471  // when we read bad memory (sometimes). Eventually we may fix this
472  // and then the scamble events should no longer occur.
473  threads.push_back( std::thread(scramble_memory, scrambleValue,
474  finishWhenThisThreadCountCompletes) );
475  break;
476  default:
477  // Default threads which just do read operations
478  // We have two modes for const and non-const iterators.
479  ++countTotalTestRuns;
480  if (bUseConstVersion) {
481  threads.push_back( std::thread(
482  do_read_operations_on_array< const Array<int> >, array_rcp,
483  setValue, scrambleValue, std::ref(index_tracker[i]),
484  maxArraySize, arrayTestStyle));
485  }
486  else {
487  threads.push_back( std::thread(
488  do_read_operations_on_array< Array<int> >, array_rcp,
489  setValue, scrambleValue, std::ref(index_tracker[i]),
490  maxArraySize, arrayTestStyle));
491  }
492  break;
493  }
494  }
495 
496  // let the threads run
497  ThreadTestManager::s_bAllowThreadsToRun = true;
498 
499  // join all threads
500  for (unsigned int i = 0; i < threads.size(); ++i) {
501  threads[i].join();
502  }
503 
504  // now count all the events from the different threads
505  for (unsigned int i = 0; i < threads.size(); ++i) {
506  // check for a danglingReference event
507  if (index_tracker[i].danglingReference != UNSET_CYCLE_INDEX ) {
508  ++countDetectedDanglingReferences;
509  }
510 
511  // Check for srambled memory events.
512  // This is the flaw we don't currently have a solution for.
513  if (index_tracker[i].scambledMemory != UNSET_CYCLE_INDEX ) {
514  ++countScrambledMemoryEvents;
515  }
516 
517  // Track out of range errors
518  if (index_tracker[i].outOfRangeError != UNSET_CYCLE_INDEX ) {
519  ++countOutOfRangeEvents;
520  }
521 
522  // This version of the test is designed to hit the dangling reference
523  // on the first cycle so we verify that happened - it provides a
524  // mode of working which is deterministic.
525  if (arrayTestStyle == ArrayTest_TriggerDanglingWithIterationFirstCycle
526  && index_tracker[i].missedDanglingOnFirstCycle != UNSET_CYCLE_INDEX ) {
527  ++countMissedFirstCycleDanglers;
528  }
529  }
530  convenience_log_progress(testCycle, numTests); // this is just output
531  }
532 
533  // some log output based on the mode of the test
534  switch (arrayTestStyle) {
535  case ArrayTest_DoReadOperations:
536  {
537  // read tests are expected to hit out of range errors
538  // We may also sometimes read bad memory - this currently does not
539  // have a solution. It happened because the weak rcp checked it was valid,
540  // thought everything was ok, then before actually reading the RCP
541  // released and the scramble thread was able to change the memory.
542  // If the scramble thread did not change the value, we may be reading
543  // bad memory which happens to still have the right stuff.
544  // So the scamble count exists to monitor that the event is taking place.
545  std::cout << "Range Errors: " << countOutOfRangeEvents <<
546  " Scrambles: " << countScrambledMemoryEvents << " ";
547  }
548  break;
549  case ArrayTest_TriggerDanglingWithIterationFirstCycle:
550  case ArrayTest_TriggerDanglingWithIteration:
551  {
552  // These two modes are expected to hit dangling references
553  // and only work with the iterators.
554  std::cout << "Danglers: " << countDetectedDanglingReferences << " ";
555  }
556  break;
557  }
558 
559  // Now decide if we passed the test. For the modes with iterators,
560  // meaning arrayTestStyle != ArrayTest_DoReadOperations,
561  // we should have detected one dangling reference on every test run exactly.
562  bool bPassed_DetectDanglers = (arrayTestStyle != ArrayTest_DoReadOperations)
563  ? (countDetectedDanglingReferences == countTotalTestRuns) : true;
564 
565  // If we rang the ArrayTest_TriggerDanglingWithIterationFirstCycle mode we
566  // tracked any time we did not hit the dangler on the first cycle by
567  // incrementing countMissedFirstCycleDanglers. That should never happen
568  // so countMissedFirstCycleDanglers should always be 0.
569  bool bPassed_DetectDanglersFirstCycle = (countMissedFirstCycleDanglers == 0);
570 
571  // ArrayTest_DoReadOperations should find out of range errors so we test
572  // that at least one was detected. The other two types use iterators and
573  // thergore should never detect out of range errors.
574  bool bPassed_CountOutOfRangeErrors =
575  (arrayTestStyle == ArrayTest_DoReadOperations) ?
576  (countOutOfRangeEvents != 0) : (countOutOfRangeEvents == 0);
577 
578  // however - there is an important difference for ArrayTest_DoReadOperations
579  // that is the only test here which has stochastic results
580  // if we are running only a few loops for the basic testing we won't
581  // necessarily pick up one of those errors and then the test can fail
582  // So here we should check if the test count is sufficiently high
583  if(NUM_TESTS_TO_RUN < 1000 && arrayTestStyle == ArrayTest_DoReadOperations) {
584  if(countOutOfRangeEvents == 0) {
585  bPassed_CountOutOfRangeErrors = true; // we allow it to go through
586  }
587  }
588 
589  // Using the above 3 bools which were set based on the test results, we can
590  // now determine if the test overall has passed. We must pass all 3.
591  bool bPass = bPassed_DetectDanglersFirstCycle &&
592  bPassed_DetectDanglersFirstCycle && bPassed_CountOutOfRangeErrors;
593 
594  if (!bPass) {
595  std::cout << std::endl; // cosmetic - get these errors on a new line
596  }
597 
598  // If we failed any of the 3 bools, we will no log some information.
599  if (!bPassed_DetectDanglers) {
600  std::cout << "Test FAILED because it detected only " <<
601  countDetectedDanglingReferences <<
602  " Dangling References but should have detected " << countTotalTestRuns
603  << "." << std::endl;
604  }
605 
606  if( !bPassed_DetectDanglersFirstCycle ) {
607  std::cout << "Test FAILED because it missed " <<
608  countMissedFirstCycleDanglers <<
609  " Dangling References but should have detected " << countTotalTestRuns
610  << " on the first cycle." << std::endl;
611  }
612 
613  if( !bPassed_CountOutOfRangeErrors ) {
614  std::cout << "Test FAILED because it detected " <<
615  countOutOfRangeEvents <<
616  " out of range events but should have detected: "
617  << ( (arrayTestStyle == ArrayTest_DoReadOperations) ?
618  "More Than 0" : "0" ) << std::endl;
619  }
620 
621  return bPass;
622 }
623 
624 // RCP Thread Safety Unit Test: mtArrayDanglingReference_NonConst_ReadValues
625 //
626 // Purpose:
627 // This mode will trigger out of range errors when we might prefer to think
628 // of them as dangling reference errors. This is just due to the internal
629 // setup and how Array<T>::assertIndex works.
630 // This may also trigger a scrambled memory event which does not have a
631 // solution implemented. This was preserved to show the event is taking
632 // place and future development may eliminate these events so it is
633 // impossible to have a weak rcp read bad memory without detection.
634 //
635 // Description:
636 // An Array is shared to multiple threads - they simultaneously try to read
637 // the Array data while one thread is constantly changing the array and
638 // another thread is constantly allocating and deleting memory.
639 //
640 // Solution to the Problem:
641 // Same as mtArrayMultipleReads_NonConst
642 //
643 // Demonstration of Original Problem
644 // Same as mtArrayMultipleReads_NonConst
645 TEUCHOS_UNIT_TEST( Array, mtArrayDanglingReference_NonConst_ReadValues )
646 {
647  bool bPass = false;
648  try {
649  bPass = runArrayDanglingReferenceTest( false,
650  ArrayTest_DoReadOperations );
651  }
652  TEUCHOS_STANDARD_CATCH_STATEMENTS(true, std::cerr, success);
653  TEST_ASSERT( bPass )
654 }
655 
656 // RCP Thread Safety Unit Test: mtArrayDanglingReference_Const_ReadValues
657 //
658 // Purpose:
659 // Similar to mtArrayDanglingReference_NonConst_ReadValues except with
660 // const iterators.
661 //
662 // Description:
663 // Similar to mtArrayDanglingReference_NonConst_ReadValues
664 //
665 // Solution to the Problem:
666 // Same as mtArrayMultipleReads_Const
667 //
668 // Demonstration of Original Problem
669 // Same as mtArrayMultipleReads_Const
670 TEUCHOS_UNIT_TEST( Array, mtArrayDanglingReference_Const_ReadValues )
671 {
672  bool bPass = false;
673  try {
674  bPass = runArrayDanglingReferenceTest( true,
675  ArrayTest_DoReadOperations );
676  }
677  TEUCHOS_STANDARD_CATCH_STATEMENTS(true, std::cerr, success);
678  TEST_ASSERT( bPass )
679 }
680 
681 // RCP Thread Safety Unit Test: mtArrayDanglingReference_NonConst
682 //
683 // Purpose:
684 // Demonstrates thread safe detection of dangling references which occur
685 // when on thread is iterating over the array while another thread is
686 // changing the size of the error (which reallocates the ArrayRCP used by
687 // the iterators.
688 //
689 // Description:
690 // An Array is shared to multiple threads - they simultaneously try to
691 // iterate over the array while other threads change the array.
692 //
693 // Solution to the Problem:
694 // Same as mtArrayMultipleReads_NonConst
695 //
696 // Demonstration of Original Problem
697 // Same as mtArrayMultipleReads_NonConst
698 TEUCHOS_UNIT_TEST( Array, mtArrayDanglingReference_NonConst )
699 {
700  bool bPass = false;
701  try {
702  bPass = runArrayDanglingReferenceTest( false,
703  ArrayTest_TriggerDanglingWithIteration );
704  }
705  TEUCHOS_STANDARD_CATCH_STATEMENTS(true, std::cerr, success);
706  TEST_ASSERT( bPass )
707 }
708 
709 // RCP Thread Safety Unit Test: mtArrayDanglingReference_Const
710 //
711 // Purpose:
712 // Same as mtArrayDanglingReference_NonConst but with const iterators.
713 //
714 // Description:
715 // Similar to mtArrayDanglingReference_NonConst.
716 //
717 // Solution to the Problem:
718 // Same as mtArrayMultipleReads_Const
719 //
720 // Demonstration of Original Problem
721 // Same as mtArrayMultipleReads_Const
722 TEUCHOS_UNIT_TEST( Array, mtArrayDanglingReference_Const )
723 {
724  bool bPass = false;
725  try {
726  bPass = runArrayDanglingReferenceTest( true,
727  ArrayTest_TriggerDanglingWithIteration );
728  }
729  TEUCHOS_STANDARD_CATCH_STATEMENTS(true, std::cerr, success);
730  TEST_ASSERT( bPass )
731 }
732 
733 // RCP Thread Safety Unit Test: mtArrayDanglingReferenceFirstCycle_NonConst
734 //
735 // Purpose:
736 // Same as mtArrayDanglingReference_NonConst but the test is designed
737 // so the dangling reference must occur on the first cycle.
738 //
739 // Description:
740 // After creating an iterator, the reading thread spin locks until it
741 // determines the thread responsible for changing the array size has
742 // completed at least one full cycle. Then the thread continues by using
743 // the iterator which is now guaranteed to be dangling.
744 //
745 // Solution to the Problem:
746 // Same as mtArrayMultipleReads_NonConst
747 //
748 // Demonstration of Original Problem
749 // Same as mtArrayMultipleReads_NonConst
750 TEUCHOS_UNIT_TEST( Array, mtArrayDanglingReferenceFirstCycle_NonConst )
751 {
752  bool bPass = false;
753  try {
754  bPass = runArrayDanglingReferenceTest( false,
755  ArrayTest_TriggerDanglingWithIterationFirstCycle );
756  }
757  TEUCHOS_STANDARD_CATCH_STATEMENTS(true, std::cerr, success);
758  TEST_ASSERT( bPass )
759 }
760 
761 // RCP Thread Safety Unit Test: mtArrayDanglingReference_Const
762 //
763 // Purpose:
764 // Similar to mtArrayDanglingReferenceFirstCycle_NonConst.
765 //
766 // Description:
767 // Similar to mtArrayDanglingReferenceFirstCycle_NonConst.
768 //
769 // Solution to the Problem:
770 // Same as mtArrayMultipleReads_Const
771 //
772 // Demonstration of Original Problem
773 // Same as mtArrayMultipleReads_Const
774 TEUCHOS_UNIT_TEST( Array, mtArrayDanglingReferenceFirstCycle_Const )
775 {
776  bool bPass = false;
777  try {
778  bPass = runArrayDanglingReferenceTest( true,
779  ArrayTest_TriggerDanglingWithIterationFirstCycle );
780  }
781  TEUCHOS_STANDARD_CATCH_STATEMENTS(true, std::cerr, success);
782  TEST_ASSERT( bPass )
783 }
784 
785 #endif // TEUCHOS_DEBUG
786 
787 } // end namespace
788 
789 
790 
Dangling reference error exception class.
RCP< T > rcp(const boost::shared_ptr< T > &sptr)
Conversion function that takes in a boost::shared_ptr object and spits out a Teuchos::RCP object...
#define TEST_ASSERT(v1)
Assert the given statement is true.
#define TEUCHOS_THREAD_SAFE_UNIT_TESTS_THREADS_USED
#define TEUCHOS_UNIT_TEST(TEST_GROUP, TEST_NAME)
Macro for defining a (non-templated) unit test.
#define UNSET_CYCLE_INDEX
#define NUM_TESTS_TO_RUN
#define TEUCHOS_STANDARD_CATCH_STATEMENTS(VERBOSE, ERR_STREAM, SUCCESS_FLAG)
Simple macro that catches and reports standard exceptions and other exceptions.
Unit testing support.
std::vector< int >::const_iterator const_iterator
The type of a const forward iterator.
Templated array class derived from the STL std::vector.
Smart reference counting pointer class for automatic garbage collection.
Range error exception class.
std::vector< int >::iterator iterator
The type of a forward iterator.
Reference-counted pointer class and non-member templated function implementations.
Replacement for std::vector that is compatible with the Teuchos Memory Management classes...