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