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;
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.
Smart reference counting pointer class for automatic garbage collection.
Ptr< T > ptr() const
Get a safer wrapper raw C++ pointer to the underlying object.
Dangling reference error exception class.
Simple wrapper class for raw pointers to single objects where no persisting relationship exists.
Ptr< T > ptrFromRef(T &arg)
Create a pointer to a object from an object reference.
Smart reference counting pointer class for automatic garbage collection.
RCP< T > rcpFromPtr(const Ptr< T > &ptr)
Create an RCP<T> from a Ptr<T> object.
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.