Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
Ptr_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 // this test is only meaningful in DEBUG and would crash in RELEASE
47 // with undefined behavior. This is because the test involves debug checks
48 // to detect weak ptrs and in release these errors are ignored. So here we
49 // are checking whether debug code can safely detectly badly written code.
50 #include "Teuchos_ConfigDefs.hpp" // get TEUCHOS_DEBUG
51 
52 #ifdef TEUCHOS_DEBUG
53 
54 #include "General_MT_UnitTests.hpp"
55 #include "Teuchos_Ptr.hpp"
58 #include <vector>
59 #include <thread>
60 #include <atomic>
61 
62 namespace {
63 
64 using Teuchos::Ptr;
65 using Teuchos::RCP;
67 using Teuchos::null;
68 using Teuchos::rcp;
69 using Teuchos::ptrFromRef;
70 using Teuchos::rcpFromPtr;
71 
72 // method used by unit test mtPtrDangling below.
73 // the thread reads the shared Ptr<int> which has been release by the
74 // main thread. The weak RCP is intended to detect when this is read after
75 // being released, which is a programmer error.
76 // The thread also puts pressue on memory by allocating/deleting ints.
77 static void share_ptr_to_threads(Ptr<int> shared_ptr, int theTestValue,
78  Cycle_Index_Tracker & index_tracker) {
79  // spin lock the threads until release by the main thread
80  while (!ThreadTestManager::s_bAllowThreadsToRun) {}
81  int cycle = 0;
82  try {
83  // If there is lots of competition for threads setting this to some
84  // safety limit of counts may fail because another thread was held up.
85  // So looping while(true) may be the cleanest and then we just
86  // time out if something goes wrong.
87  while(true) {
88  // check if the main thread has released the RCP which we point to.
89  bool bCheckStatus = ThreadTestManager::s_bMainThreadSetToNull;
90  // keep track of which cycle we are on
91  index_tracker.trackCycle = cycle;
92 
93  // Now read the ptr - there are 4 possible outcomes:
94  // (1) the ptr debug check returns dangling and a proper throw is
95  // detected - in this case we are certain of our result
96  // (2) the ptr debug check returns valid and we can read the data
97  // (because we are lucky and the data remains valid while we use it)
98  // (3) the ptr debug check returns valid, gets deleted by another
99  // thread immediately after, but we read the deleted data without
100  // knowing because it still contains the proper memory
101  // (4) the ptr debug check returns valid, gets deleted by another
102  // thread immediately after, is overwriteen by another heap
103  // allocation, and we read the scrambled data without knowing
104  if (*shared_ptr != theTestValue) {
105  index_tracker.scambledMemory = cycle; // record the cycle of the error
106  }
107 
108  // the scrambler int is trying to jump into the released memory spot
109  // through a heap allocation and disrupt the ptr value
110  int * pScramblerInt = new int;
111  *pScramblerInt = 0; // we hope to set the dangling memory space here
112  delete pScramblerInt;
113 
114  // if the main thread had released the memory before we read the ptr
115  // then we should have thrown by now. So something else has gone wrong
116  // and we record an unknown error (this currently does not every happen).
117  if (bCheckStatus) {
118  index_tracker.unknownError = cycle;
119  break;
120  }
121  ++cycle;
122  }
123  }
124  catch(DanglingReferenceError) {
125  // we got the dangling error as expected
126  index_tracker.danglingReference = cycle;
127  }
128 }
129 
130 // RCP Thread Safety Unit Test: mtPtrDangling
131 //
132 // Purpose:
133 // Validate the RCP Ptr mechanism are all thread safe.
134 // Currently Ptr can detect a dangling reference if the original RCP was
135 // released in another thread. However because it is based on a weak RCP
136 // mechanism it can be fooled and think the Ptr was valid but then before
137 // actually reading the memory, lose that value.
138 // So this test will show the danlging references are processed properly
139 // which happens almost every time. However occasionally the scrambled memory
140 // event will occur (currently no fix) and this test is designed to detect
141 // that it took place. At some point if we decide to fix this we can use this
142 // test to validate it's all working.
143 //
144 // Description:
145 // An RCP<int> is created and then a Ptr<int> is creaeted from the RCP which
146 // maintains a weak reference to the original RCP<int>. The subthreads read
147 // the Ptr<int> while the main thread releases the memory. The subthreads then
148 // detect they have a dangling reference and throw an exception.
149 //
150 // Solution to the Problem:
151 // There is currently no implemented solution for the debug situation where
152 // an RCP class attempts to dereference a weak ptr
153 //
154 // Demonstration of Problem:
155 // Running this test will provide output showing dangling reference being
156 // properly detected and a few srambled memory events occuring which currently
157 // does not have a fix in place.
158 TEUCHOS_UNIT_TEST( Ptr, mtPtrDangling )
159 {
160  const int numThreads = TEUCHOS_THREAD_SAFE_UNIT_TESTS_THREADS_USED;
161  const int numTests = NUM_TESTS_TO_RUN;
162  const int theTestValue = 1454083084; // see Ptr to arbitrary value
163  // we want to count when it's not trivial (first cycle or last cycle)
164  int countDanglingReferences = 0; // detect attempt to access deleted RCP
165  int scrambledMemoryEvents = 0; // detect scambled memory
166  int unknownErrors = 0; // detect unknown errors - currently shouldn't happen
167  for (int testCycle = 0; testCycle < numTests; ++testCycle) {
168  try {
169  // create a new int - RCP will own this int and manage its memory
170  int * pInt = new int;
171  // set the int to a test value - we will check for this value in threads
172  *pInt = theTestValue;
173  // first make an RCP
174  RCP<int> shared_rcp = rcp(pInt);
175  // now make a Ptr which remembers a weak reference to that RCP
176  Ptr<int> shared_ptr = shared_rcp.ptr();
177  // threads will start spin locked
178  ThreadTestManager::s_bAllowThreadsToRun = false;
179  // we have not yet deleted the RCP in this thread
180  ThreadTestManager::s_bMainThreadSetToNull = false;
181  // manager to keep track of events
182  Cycle_Index_Tracker index_tracker[numThreads];
183  // Now create the threads
184  std::vector<std::thread> threads;
185  for (int i = 0; i < numThreads; ++i) {
186  threads.push_back(std::thread(share_ptr_to_threads, shared_ptr,
187  theTestValue, std::ref(index_tracker[i])));
188  }
189  // let the threads run
190  ThreadTestManager::s_bAllowThreadsToRun = true;
191  // spin lock the main thread until the sub threads get started
192  while( index_tracker[0].trackCycle < 1 ) {}
193  // Now set the RCP null
194  // the RCP becomes invalid and the Ptr types all lose their valid object
195  shared_rcp = null;
196  // tell the threads the RCP is now dead
197  // This lets the threads know they 'must' detect errors on next loop
198  ThreadTestManager::s_bMainThreadSetToNull = true;
199  // Join all threads to completion
200  for (unsigned int i = 0; i < threads.size(); ++i) {
201  threads[i].join();
202  }
203  // count up all the errors
204  for (unsigned int i = 0; i < threads.size(); ++i) {
205  if (index_tracker[i].danglingReference != -1) {
206  ++countDanglingReferences; // common event
207  }
208  if (index_tracker[i].scambledMemory != -1 ) {
209  ++scrambledMemoryEvents; // happens but rarely
210  }
211  if (index_tracker[i].unknownError != -1 ) {
212  ++unknownErrors; // not presently ever an issue
213  }
214  }
215  }
216  TEUCHOS_STANDARD_CATCH_STATEMENTS(true, std::cerr, success);
217  convenience_log_progress(testCycle, numTests);// this is just output
218  }
219 
220  // verify we caught a dangler everytime
221  int expectedDanglingReferences = numThreads * numTests;
222  if( countDanglingReferences != expectedDanglingReferences) {
223  std::cout << std::endl << "Test FAILED because only " <<
224  countDanglingReferences <<
225  " dangling references were detected but expected "
226  << expectedDanglingReferences << "." << std::endl;
227  }
228  else {
229  // we got the expected number of danglers so this log is the output
230  // when the test succeeeds. We also log the number of scambled events.
231  // At some point we may implement a fix so that this missed event does not
232  // occur but that is not currently the case. The weak RCP can be tricked,
233  // think it's valid, and read the memory which subsequently was deleted.
234  // If we do implement a fix in the future, we can check here as scrambled
235  // events should then go to 0. We would then always detect invalid memory
236  // in debug mode.
237  std::cout << "Danglers: " << countDanglingReferences << " Scrambles: "
238  << scrambledMemoryEvents << " ";
239  }
240 
241  // this is not currently an issue - it was a safety check in case something
242  // unexpected ever happened in the thread loop
243  if (unknownErrors != 0) {
244  std::cout << std::endl << "Detected " << unknownErrors <<
245  " dangling references were missed which should have been detected."
246  << std::endl;
247  }
248  // verify we detected the expectedDanglingReferences
249  TEST_ASSERT(countDanglingReferences == expectedDanglingReferences)
250  // not presently an issue - this is searching for the possibility of a
251  // dangling reference missed when it should have been recorded
252  TEST_EQUALITY_CONST(unknownErrors, 0);
253 }
254 
255 } // end namespace
256 
257 #endif // TEUCHOS_DEBUG
General_MT_UnitTests.hpp
Teuchos_StandardCatchMacros.hpp
Teuchos::DanglingReferenceError
Dangling reference error exception class.
Definition: Teuchos_Exceptions.hpp:101
TEST_ASSERT
#define TEST_ASSERT(v1)
Assert the given statement is true.
Definition: Teuchos_LocalTestingHelpers.hpp:71
Teuchos::RCP::rcp
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.
NUM_TESTS_TO_RUN
#define NUM_TESTS_TO_RUN
Definition: Array_MT_Nightly_UnitTests.cpp:44
Teuchos::rcp
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.
Definition: Teuchos_RCPDecl.hpp:1224
Teuchos::RCP
Smart reference counting pointer class for automatic garbage collection.
Definition: Teuchos_RCPDecl.hpp:429
Teuchos::Ptr
Simple wrapper class for raw pointers to single objects where no persisting relationship exists.
Definition: Teuchos_PtrDecl.hpp:104
Teuchos_UnitTestHarness.hpp
Unit testing support.
Teuchos_Ptr.hpp
TEUCHOS_UNIT_TEST
#define TEUCHOS_UNIT_TEST(TEST_GROUP, TEST_NAME)
Macro for defining a (non-templated) unit test.
Definition: Teuchos_UnitTestHelpers.hpp:83
TEST_EQUALITY_CONST
#define TEST_EQUALITY_CONST(v1, v2)
Assert the equality of v1 and constant v2.
Definition: Teuchos_LocalTestingHelpers.hpp:79
Teuchos_ConfigDefs.hpp
Teuchos header file which uses auto-configuration information to include necessary C++ headers.
TEUCHOS_THREAD_SAFE_UNIT_TESTS_THREADS_USED
#define TEUCHOS_THREAD_SAFE_UNIT_TESTS_THREADS_USED
Definition: General_MT_UnitTests.hpp:51
Teuchos::null
Definition: Teuchos_ENull.hpp:54
TEUCHOS_STANDARD_CATCH_STATEMENTS
#define TEUCHOS_STANDARD_CATCH_STATEMENTS(VERBOSE, ERR_STREAM, SUCCESS_FLAG)
Simple macro that catches and reports standard exceptions and other exceptions.
Definition: Teuchos_StandardCatchMacros.hpp:136