Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
Teuchos_RCPNode.cpp
Go to the documentation of this file.
1 // @HEADER
2 // ***********************************************************************
3 //
4 // Teuchos: Common Tools Package
5 // Copyright (2004) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
38 //
39 // ***********************************************************************
40 // @HEADER
41 
42 #include "Teuchos_RCPNode.hpp"
43 #include "Teuchos_Assert.hpp"
44 #include "Teuchos_Exceptions.hpp"
45 #include <vector>
46 
47 #ifdef TEUCHOS_DEBUG
49 #endif
50 
51 // Defined this to see tracing of RCPNodes created and destroyed
52 //#define RCP_NODE_DEBUG_TRACE_PRINT
53 
54 
55 //
56 // Internal implementatation stuff
57 //
58 
59 #if defined(TEUCHOS_DEBUG) && defined(HAVE_TEUCHOSCORE_CXX11) && defined(HAVE_TEUCHOS_THREAD_SAFE)
60 #include <mutex>
61 #define USE_MUTEX_TO_PROTECT_NODE_TRACING
62 #endif
63 
64 namespace {
65 
66 
67 //
68 // Local implementation types
69 //
70 
71 
72 struct RCPNodeInfo {
73  RCPNodeInfo() = delete;
74  RCPNodeInfo(const std::string &info_in, Teuchos::RCPNode* nodePtr_in)
75  : info(info_in), nodePtr(nodePtr_in)
76  {}
77  std::string info;
78  Teuchos::RCPNode* nodePtr;
79 };
80 
81 
82 typedef std::pair<const void*, RCPNodeInfo> VoidPtrNodeRCPInfoPair_t;
83 
84 
85 typedef std::multimap<const void*, RCPNodeInfo> rcp_node_list_t;
86 
87 //
88 // Local static functions returning references to local static objects to
89 // ensure objects are initilaized.
90 //
91 // Technically speaking, the static functions on RCPNodeTracer that use this
92 // data might be called from other translation units in pre-main code before
93 // this translation unit gets initialized. By using functions returning
94 // references to local static variable trick, we ensure that these objects are
95 // always initialized before they are used, no matter what.
96 //
97 // These could have been static functions on RCPNodeTracer but the advantage
98 // of defining these functions this way is that you can add and remove
99 // functions without affecting the *.hpp file and therefore avoid
100 // recompilation (and even relinking with shared libraries).
101 //
102 
103 
104 rcp_node_list_t*& rcp_node_list()
105 {
106  static rcp_node_list_t *s_rcp_node_list = 0;
107  // Here we must let the ActiveRCPNodesSetup constructor and destructor handle
108  // the creation and destruction of this map object. This will ensure that
109  // this map object will be valid when any global/static RCP objects are
110  // destroyed! Note that this object will get created and destroyed
111  // reguardless if whether we are tracing RCPNodes or not. This just makes our
112  // life simpler. NOTE: This list will always get allocated no mater if
113  // TEUCHOS_DEBUG is defined or node traceing is enabled or not.
114  return s_rcp_node_list;
115 }
116 
117 #ifdef USE_MUTEX_TO_PROTECT_NODE_TRACING
118 std::mutex *& rcp_node_list_mutex()
119 {
120  static std::mutex * s_rcp_node_list_mutex = 0;
121  // This construct exists for the same reason as above (rcp_node_list)
122  // We must keep this mutex in place until all static RCP objects have deleted.
123  return s_rcp_node_list_mutex;
124 }
125 #endif
126 
127 bool& loc_isTracingActiveRCPNodes()
128 {
129  static bool s_loc_isTracingActiveRCPNodes =
130 #if defined(TEUCHOS_DEBUG) && defined(HAVE_TEUCHOS_DEBUG_RCP_NODE_TRACING)
131  true
132 #else
133  false
134 #endif
135  ;
136  return s_loc_isTracingActiveRCPNodes;
137 }
138 
139 
140 Teuchos::RCPNodeTracer::RCPNodeStatistics& loc_rcpNodeStatistics()
141 {
142  static Teuchos::RCPNodeTracer::RCPNodeStatistics s_loc_rcpNodeStatistics;
143  return s_loc_rcpNodeStatistics;
144 }
145 
146 
147 bool& loc_printRCPNodeStatisticsOnExit()
148 {
149  static bool s_loc_printRCPNodeStatisticsOnExit = false;
150  return s_loc_printRCPNodeStatisticsOnExit;
151 }
152 
153 
154 bool& loc_printActiveRcpNodesOnExit()
155 {
156  static bool s_loc_printActiveRcpNodesOnExit = true;
157  return s_loc_printActiveRcpNodesOnExit;
158 }
159 
160 
161 //
162 // Other helper functions
163 //
164 
165 // This function returns the const void* value that is used as the key to look
166 // up an RCPNode object that has been stored. If the RCPNode is holding a
167 // non-null reference, then we use that object address as the key. That way,
168 // we can detect if a user trys to create a new owning RCPNode to the same
169 // object. If the RCPNode has an null internal object pointer, then we will
170 // use the RCPNode's address itself. In this case, we want to check and see
171 // that all RCPNodes that get created get destroyed correctly.
172 const void* get_map_key_void_ptr(const Teuchos::RCPNode* rcp_node)
173 {
174  TEUCHOS_ASSERT(rcp_node);
175 #ifdef TEUCHOS_DEBUG
176  const void* base_obj_map_key_void_ptr = rcp_node->get_base_obj_map_key_void_ptr();
177  if (base_obj_map_key_void_ptr)
178  return base_obj_map_key_void_ptr;
179 #endif
180  return rcp_node;
181 }
182 
183 
184 std::string convertRCPNodeToString(const Teuchos::RCPNode* rcp_node)
185 {
186  std::ostringstream oss;
187  oss
188  << "RCPNode {address="
189  << rcp_node
190 #ifdef TEUCHOS_DEBUG
191  << ", base_obj_map_key_void_ptr=" << rcp_node->get_base_obj_map_key_void_ptr()
192 #endif
193  << ", base_obj_type_name=" << rcp_node->get_base_obj_type_name()
194  << ", map_key_void_ptr=" << get_map_key_void_ptr(rcp_node)
195  << ", has_ownership=" << rcp_node->has_ownership()
196 #ifdef TEUCHOS_DEBUG
197  << ", insertionNumber="<< rcp_node->insertion_number()
198 #endif
199  << "}";
200  return oss.str();
201 }
202 
203 
204 } // namespace
205 
206 
207 namespace Teuchos {
208 
209 
210 //
211 // RCPNode
212 //
213 
214 
216  const any &extra_data, const std::string& name
217  ,EPrePostDestruction destroy_when
218  ,bool force_unique
219  )
220 {
221  if(extra_data_map_==NULL) {
223  }
224  const std::string type_and_name( extra_data.typeName() + std::string(":") + name );
225  extra_data_map_t::iterator itr = extra_data_map_->find(type_and_name);
226 #ifdef TEUCHOS_DEBUG
228  (itr != extra_data_map_->end() && force_unique), std::invalid_argument
229  ,"Error, the type:name pair \'" << type_and_name
230  << "\' already exists and force_unique==true!" );
231 #endif
232  if (itr != extra_data_map_->end()) {
233  // Change existing extra data
234  itr->second = extra_data_entry_t(extra_data,destroy_when);
235  }
236  else {
237  // Insert new extra data
238  (*extra_data_map_)[type_and_name] =
239  extra_data_entry_t(extra_data,destroy_when);
240  }
241 }
242 
243 
244 any& RCPNode::get_extra_data( const std::string& type_name, const std::string& name )
245 {
246 #ifdef TEUCHOS_DEBUG
248  extra_data_map_==NULL, std::invalid_argument
249  ,"Error, no extra data has been set yet!" );
250 #endif
251  any *extra_data = get_optional_extra_data(type_name,name);
252 #ifdef TEUCHOS_DEBUG
253  if (!extra_data) {
254  const std::string type_and_name( type_name + std::string(":") + name );
256  extra_data == NULL, std::invalid_argument
257  ,"Error, the type:name pair \'" << type_and_name << "\' is not found!" );
258  }
259 #endif
260  return *extra_data;
261 }
262 
263 
264 any* RCPNode::get_optional_extra_data( const std::string& type_name,
265  const std::string& name )
266 {
267  if( extra_data_map_ == NULL ) return NULL;
268  const std::string type_and_name( type_name + std::string(":") + name );
269  extra_data_map_t::iterator itr = extra_data_map_->find(type_and_name);
270  if(itr != extra_data_map_->end())
271  return &(*itr).second.extra_data;
272  return NULL;
273 }
274 
275 
277 {
278  for(
279  extra_data_map_t::iterator itr = extra_data_map_->begin();
280  itr != extra_data_map_->end();
281  ++itr
282  )
283  {
284  extra_data_map_t::value_type &entry = *itr;
285  if(entry.second.destroy_when == PRE_DESTROY)
286  entry.second.extra_data = any();
287  }
288 }
289 
290 
291 //
292 // RCPNodeTracer
293 //
294 
295 
296 // General user functions
297 
298 
300 {
301  return loc_isTracingActiveRCPNodes();
302 }
303 
304 
305 #if defined(TEUCHOS_DEBUG) && !defined(HAVE_TEUCHOS_DEBUG_RCP_NODE_TRACING)
306 void RCPNodeTracer::setTracingActiveRCPNodes(bool tracingActiveNodes)
307 {
308  loc_isTracingActiveRCPNodes() = tracingActiveNodes;
309 }
310 #endif
311 
312 
314 {
315  // This list always exists, no matter debug or not so just access it.
316  TEUCHOS_TEST_FOR_EXCEPT(0==rcp_node_list());
317  return static_cast<int>(rcp_node_list()->size());
318 }
319 
320 
323 {
324  return loc_rcpNodeStatistics();
325 }
326 
328  const RCPNodeStatistics& rcpNodeStatistics, std::ostream &out)
329 {
330  out
331  << "\n***"
332  << "\n*** RCPNode Tracing statistics:"
333  << "\n**\n"
334  << "\n maxNumRCPNodes = "<<rcpNodeStatistics.maxNumRCPNodes
335  << "\n totalNumRCPNodeAllocations = "<<rcpNodeStatistics.totalNumRCPNodeAllocations
336  << "\n totalNumRCPNodeDeletions = "<<rcpNodeStatistics.totalNumRCPNodeDeletions
337  << "\n";
338 }
339 
340 
342  bool printRCPNodeStatisticsOnExit)
343 {
344  loc_printRCPNodeStatisticsOnExit() = printRCPNodeStatisticsOnExit;
345 }
346 
347 
349 {
350  return loc_printRCPNodeStatisticsOnExit();
351 }
352 
353 
354 void RCPNodeTracer::setPrintActiveRcpNodesOnExit(bool printActiveRcpNodesOnExit)
355 {
356  loc_printActiveRcpNodesOnExit() = printActiveRcpNodesOnExit;
357 }
358 
359 
361 {
362  return loc_printActiveRcpNodesOnExit();
363 }
364 
365 
366 void RCPNodeTracer::printActiveRCPNodes(std::ostream &out)
367 {
368 #ifdef TEUCHOS_SHOW_ACTIVE_REFCOUNTPTR_NODE_TRACE
369  out
370  << "\nCalled printActiveRCPNodes() :"
371  << " rcp_node_list.size() = " << rcp_node_list().size() << "\n";
372 #endif // TEUCHOS_SHOW_ACTIVE_REFCOUNTPTR_NODE_TRACE
373  if (loc_isTracingActiveRCPNodes()) {
374  TEUCHOS_TEST_FOR_EXCEPT(0==rcp_node_list());
375  if (rcp_node_list()->size() > 0) {
377  // Create a sorted-by-insertionNumber list
378  // NOTE: You have to use std::vector and *not* Teuchos::Array rcp here
379  // because this called at the very end and uses RCPNode itself in a
380  // debug-mode build.
381  typedef std::vector<VoidPtrNodeRCPInfoPair_t> rcp_node_vec_t;
382  rcp_node_vec_t rcp_node_vec(rcp_node_list()->begin(), rcp_node_list()->end());
383  std::sort(rcp_node_vec.begin(), rcp_node_vec.end(),
384  [] (const rcp_node_list_t::value_type &v1, const rcp_node_list_t::value_type &v2)
385  {
386 #ifdef TEUCHOS_DEBUG
387  return v1.second.nodePtr->insertion_number() < v2.second.nodePtr->insertion_number();
388 #else
389  return v1.first < v2.first;
390 #endif
391  }
392  );
393  // Print the RCPNode objects sorted by insertion number
394  typedef rcp_node_vec_t::const_iterator itr_t;
395  int i = 0;
396  for ( itr_t itr = rcp_node_vec.begin(); itr != rcp_node_vec.end(); ++itr ) {
397  const rcp_node_list_t::value_type &entry = *itr;
398  TEUCHOS_ASSERT(entry.second.nodePtr);
399  out
400  << "\n"
401  << std::setw(3) << std::right << i << std::left
402  << ": RCPNode (map_key_void_ptr=" << entry.first << ")\n"
403  << " Information = " << entry.second.info << "\n"
404  << " RCPNode address = " << entry.second.nodePtr << "\n"
405 #ifdef TEUCHOS_DEBUG
406  << " insertionNumber = " << entry.second.nodePtr->insertion_number()
407 #endif
408  ;
409  ++i;
410  }
411  out << "\n\n"
413  }
414  }
415 }
416 
417 
418 // Internal implementation functions
419 
420 
421 void RCPNodeTracer::addNewRCPNode( RCPNode* rcp_node, const std::string &info )
422 {
423 #ifdef USE_MUTEX_TO_PROTECT_NODE_TRACING
424  // lock_guard will unlock in the event of an exception
425  std::lock_guard<std::mutex> lockGuard(*rcp_node_list_mutex());
426 #endif // USE_MUTEX_TO_PROTECT_NODE_TRACING
427 
428  // Used to allow unique identification of rcp_node to allow setting breakpoints
429  static int insertionNumber = 0;
430 
431  // Set the insertion number right away in case an exception gets thrown so
432  // that you can set a break point to debug this.
433 #ifdef TEUCHOS_DEBUG
434  rcp_node->set_insertion_number(insertionNumber);
435 #endif
436 
437  if (loc_isTracingActiveRCPNodes()) {
438 
439  // Print the node we are adding if configured to do so. We have to send
440  // to std::cerr to make sure that this gets printed.
441 #ifdef RCP_NODE_DEBUG_TRACE_PRINT
442  std::cerr
443  << "RCPNodeTracer::addNewRCPNode(...): Adding "
444  << convertRCPNodeToString(rcp_node) << " ...\n";
445 #endif
446 
447  TEUCHOS_TEST_FOR_EXCEPT(0==rcp_node_list());
448 
449  const void * const map_key_void_ptr = get_map_key_void_ptr(rcp_node);
450 
451  // See if the rcp_node or its object has already been added.
452  typedef rcp_node_list_t::iterator itr_t;
453  typedef std::pair<itr_t, itr_t> itr_itr_t;
454  const itr_itr_t itr_itr = rcp_node_list()->equal_range(map_key_void_ptr);
455  const bool rcp_node_already_exists = itr_itr.first != itr_itr.second;
456  RCPNode *previous_rcp_node = 0;
457  bool previous_rcp_node_has_ownership = false;
458  for (itr_t itr = itr_itr.first; itr != itr_itr.second; ++itr) {
459  previous_rcp_node = itr->second.nodePtr;
460  if (previous_rcp_node->has_ownership()) {
461  previous_rcp_node_has_ownership = true;
462  break;
463  }
464  }
466  rcp_node_already_exists && rcp_node->has_ownership() && previous_rcp_node_has_ownership,
468  "RCPNodeTracer::addNewRCPNode(rcp_node): Error, the client is trying to create a new\n"
469  "RCPNode object to an existing managed object in another RCPNode:\n"
470  "\n"
471  " New " << convertRCPNodeToString(rcp_node) << "\n"
472  "\n"
473  " Existing " << convertRCPNodeToString(previous_rcp_node) << "\n"
474  "\n"
475  " Number current nodes = " << rcp_node_list()->size() << "\n"
476  "\n"
477  "This may indicate that the user might be trying to create a weak RCP to an existing\n"
478  "object but forgot make it non-ownning. Perhaps they meant to use rcpFromRef(...)\n"
479  "or an equivalent function?\n"
480  "\n"
482  );
483 
484  // NOTE: We allow duplicate RCPNodes if the new node is non-owning. This
485  // might indicate a advanced usage of the RCP class that we want to
486  // support. The typical problem is when the programmer unknowingly
487  // creates an owning RCP to an object already owned by another RCPNode.
488 
489  // Add the new RCP node keyed as described above.
490  (*rcp_node_list()).emplace_hint(
491  itr_itr.second,
492  std::make_pair(map_key_void_ptr, RCPNodeInfo(info, rcp_node))
493  );
494  // NOTE: Above, if there is already an existing RCPNode with the same key
495  // value, this iterator itr_itr.second will point to one after the found
496  // range. I suspect that this might also ensure that the elements are
497  // sorted in natural order.
498 
499  // Update the insertion number an node tracing statistics
500  ++insertionNumber;
501  ++loc_rcpNodeStatistics().totalNumRCPNodeAllocations;
502  loc_rcpNodeStatistics().maxNumRCPNodes =
503  TEUCHOS_MAX(loc_rcpNodeStatistics().maxNumRCPNodes, numActiveRCPNodes());
504  }
505 }
506 
507 
508 #define TEUCHOS_RCPNODE_REMOVE_RCPNODE(CONDITION, RCPNODE) \
509  TEUCHOS_TEST_FOR_EXCEPTION((CONDITION), \
510  std::logic_error, \
511  "RCPNodeTracer::removeRCPNode(node_ptr): Error, the " \
512  << convertRCPNodeToString(RCPNODE) << " is not found in the list of" \
513  " active RCP nodes being traced even though all nodes should be traced." \
514  " This should not be possible and can only be an internal programming error!")
515 
516 
518 {
519 
520  // Here, we will try to remove an RCPNode reguardless if whether
521  // loc_isTracingActiveRCPNodes==true or not. This will not be a performance
522  // problem and it will ensure that any RCPNode objects that are added to
523  // this list will be removed and will not look like a memory leak. In
524  // non-debug mode, this function will never be called. In debug mode, with
525  // loc_isTracingActiveRCPNodes==false, the list *rcp_node_list will be empty and
526  // therefore this find(...) operation should be pretty cheap (even for a bad
527  // implementation of std::map).
528 
529 #ifdef USE_MUTEX_TO_PROTECT_NODE_TRACING
530  // lock_guard will unlock in the event of an exception
531  std::lock_guard<std::mutex> lockGuard(*rcp_node_list_mutex());
532 #endif // USE_MUTEX_TO_PROTECT_NODE_TRACING
533 
534  TEUCHOS_ASSERT(rcp_node_list());
535 
536  typedef rcp_node_list_t::iterator itr_t;
537  typedef std::pair<itr_t, itr_t> itr_itr_t;
538 
539  const itr_itr_t itr_itr =
540  rcp_node_list()->equal_range(get_map_key_void_ptr(rcp_node));
541  const bool rcp_node_exists = itr_itr.first != itr_itr.second;
542 
543 #ifdef HAVE_TEUCHOS_DEBUG_RCP_NODE_TRACING
544  // If we have the macro HAVE_TEUCHOS_DEBUG_RCP_NODE_TRACING turned on a
545  // compile time, then all RCPNode objects that get created will have been
546  // added to this list. In this case, we can asset that the node exists.
547  TEUCHOS_RCPNODE_REMOVE_RCPNODE(!rcp_node_exists, rcp_node);
548 #else
549  // If the macro HAVE_TEUCHOS_DEBUG_RCP_NODE_TRACING turned off, then is is
550  // possible that an RCP got created before the bool
551  // loc_isTracingActiveRCPNodes was turned on. In this case, we must allow
552  // for an RCP node not to have been added to this list. In this case we
553  // will just let this go!
554 #endif
555 
556  if (rcp_node_exists) {
557 #ifdef RCP_NODE_DEBUG_TRACE_PRINT
558  std::cerr
559  << "RCPNodeTracer::removeRCPNode(...): Removing "
560  << convertRCPNodeToString(rcp_node) << " ...\n";
561 #endif
562  bool foundRCPNode = false;
563  for(itr_t itr = itr_itr.first; itr != itr_itr.second; ++itr) {
564  if (itr->second.nodePtr == rcp_node) {
565  rcp_node_list()->erase(itr);
566  ++loc_rcpNodeStatistics().totalNumRCPNodeDeletions;
567  foundRCPNode = true;
568  break;
569  }
570  }
571  // Whoops! Did not find the node!
572  TEUCHOS_RCPNODE_REMOVE_RCPNODE(!foundRCPNode, rcp_node);
573  }
574 
575 }
576 
577 
579 {
580  typedef rcp_node_list_t::iterator itr_t;
581  typedef std::pair<itr_t, itr_t> itr_itr_t;
582  if (!p)
583  return 0;
584 
585 #ifdef USE_MUTEX_TO_PROTECT_NODE_TRACING
586  // lock_guard will unlock in the event of an exception
587  std::lock_guard<std::mutex> lockGuard(*rcp_node_list_mutex());
588 #endif // USE_MUTEX_TO_PROTECT_NODE_TRACING
589 
590  const itr_itr_t itr_itr = rcp_node_list()->equal_range(p);
591  for (itr_t itr = itr_itr.first; itr != itr_itr.second; ++itr) {
592  RCPNode* rcpNode = itr->second.nodePtr;
593  if (rcpNode->has_ownership()) {
594  return rcpNode;
595  }
596  }
597  return 0;
598  // NOTE: Above, we return the first RCPNode added that has the given key
599  // value.
600 }
601 
602 
604 {
605  return std::string(
606  "\n***"
607  "\n*** Warning! The following Teuchos::RCPNode objects were created but have"
608  "\n*** not been destroyed yet. A memory checking tool may complain that these"
609  "\n*** objects are not destroyed correctly."
610  "\n***"
611  "\n*** There can be many possible reasons that this might occur including:"
612  "\n***"
613  "\n*** a) The program called abort() or exit() before main() was finished."
614  "\n*** All of the objects that would have been freed through destructors"
615  "\n*** are not freed but some compilers (e.g. GCC) will still call the"
616  "\n*** destructors on static objects (which is what causes this message"
617  "\n*** to be printed)."
618  "\n***"
619  "\n*** b) The program is using raw new/delete to manage some objects and"
620  "\n*** delete was not called correctly and the objects not deleted hold"
621  "\n*** other objects through reference-counted pointers."
622  "\n***"
623  "\n*** c) This may be an indication that these objects may be involved in"
624  "\n*** a circular dependency of reference-counted managed objects."
625  "\n***\n"
626  );
627 }
628 
629 
631 {
632  return std::string(
633  "NOTE: To debug issues, open a debugger, and set a break point in the function where\n"
634  "the RCPNode object is first created to determine the context where the object first\n"
635  "gets created. Each RCPNode object is given a unique insertionNumber to allow setting\n"
636  "breakpoints in the code. For example, in GDB one can perform:\n"
637  "\n"
638  "1) Open the debugger (GDB) and run the program again to get updated object addresses\n"
639  "\n"
640  "2) Set a breakpoint in the RCPNode insertion routine when the desired RCPNode is first\n"
641  "inserted. In GDB, to break when the RCPNode with insertionNumber==3 is added, do:\n"
642  "\n"
643  " (gdb) b 'Teuchos::RCPNodeTracer::addNewRCPNode( [TAB] ' [ENTER]\n"
644  " (gdb) cond 1 insertionNumber==3 [ENTER]\n"
645  "\n"
646  "3) Run the program in the debugger. In GDB, do:\n"
647  "\n"
648  " (gdb) run [ENTER]\n"
649  "\n"
650  "4) Examine the call stack when the program breaks in the function addNewRCPNode(...)\n"
651  );
652 }
653 
654 
655 //
656 // ActiveRCPNodesSetup
657 //
658 
659 
661 {
662 #ifdef TEUCHOS_SHOW_ACTIVE_REFCOUNTPTR_NODE_TRACE
663  std::cerr << "\nCalled ActiveRCPNodesSetup::ActiveRCPNodesSetup() : count = " << count_ << "\n";
664 #endif // TEUCHOS_SHOW_ACTIVE_REFCOUNTPTR_NODE_TRACE
665  if (!rcp_node_list())
666  rcp_node_list() = new rcp_node_list_t;
667 
668 #ifdef USE_MUTEX_TO_PROTECT_NODE_TRACING
669  if (!rcp_node_list_mutex()) {
670  rcp_node_list_mutex() = new std::mutex;
671  }
672 #endif
673  ++count_;
674 }
675 
676 
678 {
679 #ifdef TEUCHOS_SHOW_ACTIVE_REFCOUNTPTR_NODE_TRACE
680  std::cerr << "\nCalled ActiveRCPNodesSetup::~ActiveRCPNodesSetup() : count = " << count_ << "\n";
681 #endif // TEUCHOS_SHOW_ACTIVE_REFCOUNTPTR_NODE_TRACE
682  if( --count_ == 0 ) {
683 #ifdef TEUCHOS_SHOW_ACTIVE_REFCOUNTPTR_NODE_TRACE
684  std::cerr << "\nPrint active nodes!\n";
685 #endif // TEUCHOS_SHOW_ACTIVE_REFCOUNTPTR_NODE_TRACE
686  std::cout << std::flush;
687  TEUCHOS_TEST_FOR_TERMINATION(nullptr==rcp_node_list(), "rcp_node_list() is null in ~ActiveRCPNodesSetup");
688  RCPNodeTracer::RCPNodeStatistics rcpNodeStatistics =
690  if (rcpNodeStatistics.maxNumRCPNodes
692  {
693  RCPNodeTracer::printRCPNodeStatistics(rcpNodeStatistics, std::cout);
694  }
697  }
698  delete rcp_node_list();
699  rcp_node_list() = 0;
700 
701 #ifdef USE_MUTEX_TO_PROTECT_NODE_TRACING
702  delete rcp_node_list_mutex();
703  rcp_node_list_mutex() = 0;
704 #endif
705  }
706 }
707 
708 
710 {
711  int dummy = count_;
712  ++dummy; // Avoid unused variable warning (bug 2664)
713 }
714 
715 
717 
718 
719 //
720 // RCPNodeHandle
721 //
722 
724 {
725 #ifdef TEUCHOS_DEBUG
727 #endif
728  // do this after removeRCPNode - otherwise another thread can jump in and grab
729  // the memory - then node tracing incorrectly thinks it's a double allocation
730  node_->delete_obj();
731 }
732 
734 {
735  delete node_;
736  node_ = 0;
737 }
738 
739 } // namespace Teuchos
740 
741 
742 //
743 // Non-member helpers
744 //
745 
746 
747 void Teuchos::throw_null_ptr_error( const std::string &type_name )
748 {
750  true, NullReferenceError,
751  type_name << " : You can not call operator->() or operator*()"
752  <<" if getRawPtr()==0!" );
753 }
754 
755 // Implement abort and exception handling for RCPNode
756 // Note "PROGRAM ABORTING" text will be checked in a unit test and to
757 // avoid having a more complex code here to ensure no mixed output, I kept that as 1 MPI.
758 // if(!success) added to prevent DEBUG unused variable warning.
759 #ifdef TEUCHOS_DEBUG
760 #define TEUCHOS_IMPLEMENT_ABORT(excpt) \
761  bool success = false; \
762  try { throw excpt; } \
763  TEUCHOS_STANDARD_CATCH_STATEMENTS(true,std::cerr,success); \
764  if(!success) std::cerr << "PROGRAM ABORTING\n"; \
765  GlobalMPISession::abort();
766 
767 void Teuchos::abort_for_exception_in_destructor(const std::exception &exception) {
768  TEUCHOS_IMPLEMENT_ABORT(exception);
769 }
770 void Teuchos::abort_for_exception_in_destructor(const int &code) {
771  TEUCHOS_IMPLEMENT_ABORT(code);
772 }
773 void Teuchos::abort_for_exception_in_destructor() {
774  TEUCHOS_IMPLEMENT_ABORT(std::logic_error(
775  "Caught unknown exception from destructor of RCPNode. Aborting."););
776 }
777 #endif // TEUCHOS_DEBUG
static void printRCPNodeStatistics(const RCPNodeStatistics &rcpNodeStatistics, std::ostream &out)
Print the RCPNode allocation statistics.
void impl_pre_delete_extra_data()
static bool isTracingActiveRCPNodes()
Return if we are tracing active nodes or not.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
static void setPrintRCPNodeStatisticsOnExit(bool printRCPNodeStatisticsOnExit)
Set if RCPNode usage statistics will be printed when the program ends or not.
void set_extra_data(const any &extra_data, const std::string &name, EPrePostDestruction destroy_when, bool force_unique)
static RCPNodeStatistics getRCPNodeStatistics()
Return the statistics on RCPNode allocations.
Modified boost::any class, which is a container for a templated value.
any & get_extra_data(const std::string &type_name, const std::string &name)
void has_ownership(bool has_ownership_in)
static std::string getCommonDebugNotesString()
Common error message string on how to debug RCPNode problems.
static bool getPrintRCPNodeStatisticsOnExit()
Return if RCPNode usage statistics will be printed when the program ends or not.
Node class to keep track of address and the reference count for a reference-counted utility class and...
static RCPNode * getExistingRCPNodeGivenLookupKey(const void *lookupKey)
Return a raw pointer to an existing owning RCPNode given its lookup key.
static bool getPrintActiveRcpNodesOnExit()
Return if printActiveRCPNodes() is called on exit from the program.
#define TEUCHOS_MAX(x, y)
virtual const std::string get_base_obj_type_name() const =0
#define TEUCHOS_RCPNODE_REMOVE_RCPNODE(CONDITION, RCPNODE)
EPrePostDestruction
Used to specify a pre or post destruction of extra data.
virtual void delete_obj()=0
std::string typeName() const
Return the name of the type.
#define TEUCHOS_TEST_FOR_TERMINATION(terminate_test, msg)
This macro is to be used instead of TEUCHOS_TEST_FOR_EXCEPTION() to report an error in situations whe...
any * get_optional_extra_data(const std::string &type_name, const std::string &name)
static void removeRCPNode(RCPNode *rcp_node)
Remove an RCPNode from global list.
extra_data_map_t * extra_data_map_
#define TEUCHOS_ASSERT(assertion_test)
This macro is throws when an assert fails.
Teuchos::map< std::string, extra_data_entry_t > extra_data_map_t
Reference-counted pointer node classes.
Thrown if a duplicate owning RCP is creatd the the same object.
static std::string getActiveRCPNodeHeaderString()
Header string used in printActiveRCPNodes().
static int numActiveRCPNodes()
Print the number of active RCPNode objects currently being tracked.
static void addNewRCPNode(RCPNode *rcp_node, const std::string &info)
Add new RCPNode to the global list.
#define TEUCHOS_TEST_FOR_EXCEPT(throw_exception_test)
This macro is designed to be a short version of TEUCHOS_TEST_FOR_EXCEPTION() that is easier to call...
static void printActiveRCPNodes(std::ostream &out)
Print the list of currently active RCP nodes.
static void setPrintActiveRcpNodesOnExit(bool printActiveRcpNodesOnExit)
Set if printActiveRCPNodes() is called on exit from the program.