Teuchos Package Browser (Single Doxygen Collection) Version of the Day
Loading...
Searching...
No Matches
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
55#include "Teuchos_Ptr.hpp"
58#include <vector>
59#include <thread>
60#include <atomic>
61
62namespace {
63
64using Teuchos::Ptr;
65using Teuchos::RCP;
67using Teuchos::null;
68using Teuchos::rcp;
69using Teuchos::ptrFromRef;
70using 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.
77static 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.
158TEUCHOS_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
#define NUM_TESTS_TO_RUN
#define TEUCHOS_THREAD_SAFE_UNIT_TESTS_THREADS_USED
Teuchos header file which uses auto-configuration information to include necessary C++ headers.
#define TEST_ASSERT(v1)
Assert the given statement is true.
#define TEST_EQUALITY_CONST(v1, v2)
Assert the equality of v1 and constant v2.
#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 TEUCHOS_UNIT_TEST(TEST_GROUP, TEST_NAME)
Macro for defining a (non-templated) unit test.
Dangling reference error exception class.
Simple wrapper class for raw pointers to single objects where no persisting relationship exists.
Smart reference counting pointer class for automatic garbage collection.
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.