Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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
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
Teuchos header file which uses auto-configuration information to include necessary C++ headers...
#define TEUCHOS_UNIT_TEST(TEST_GROUP, TEST_NAME)
Macro for defining a (non-templated) unit test.
#define NUM_TESTS_TO_RUN
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.
#define TEUCHOS_STANDARD_CATCH_STATEMENTS(VERBOSE, ERR_STREAM, SUCCESS_FLAG)
Simple macro that catches and reports standard exceptions and other exceptions.
Unit testing support.
#define TEST_EQUALITY_CONST(v1, v2)
Assert the equality of v1 and constant v2.
Smart reference counting pointer class for automatic garbage collection.
Simple wrapper class for raw pointers to single objects where no persisting relationship exists...