]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Update description of the Subscriptor-SmartPointer pair in the examples 7410/head
authorDaniel Arndt <daniel.arndt@iwr.uni-heidelberg.de>
Fri, 2 Nov 2018 23:17:13 +0000 (00:17 +0100)
committerDaniel Arndt <daniel.arndt@iwr.uni-heidelberg.de>
Mon, 5 Nov 2018 08:20:39 +0000 (09:20 +0100)
examples/step-13/step-13.cc
examples/step-37/step-37.cc
examples/step-6/doc/results.dox
examples/step-6/step-6.cc
examples/step-60/step-60.cc
examples/step-7/step-7.cc

index 3e4fa29104674cd44493f50b0088b3c022937fed..b37aac8107ab1979c2da863305fe8d194e6a61a8 100644 (file)
@@ -476,12 +476,9 @@ namespace Step13
     // therefore just declare such an abstract base class, taking a pointer to
     // a triangulation in the constructor and storing it henceforth. Since
     // this triangulation will be used throughout all computations, we have to
-    // make sure that the triangulation exists until the destructor exits. We
+    // make sure that the triangulation is valid until it is last used. We
     // do this by keeping a <code>SmartPointer</code> to this triangulation,
-    // which uses a counter in the triangulation class to denote the fact that
-    // there is still an object out there using this triangulation, thus
-    // leading to an abort in case the triangulation is attempted to be
-    // destroyed while this object still uses it.
+    // as explained in step-7.
     //
     // Note that while the pointer itself is declared constant
     // (i.e. throughout the lifetime of this object, the pointer points to the
index facd794e4f60ddeb09734043b3fd7c17a5c796bc..65d118b59b30c07672148d5ef0a102ad54143df5 100644 (file)
@@ -254,8 +254,8 @@ namespace Step37
   // This is the constructor of the @p LaplaceOperator class. All it does is
   // to call the default constructor of the base class
   // MatrixFreeOperators::Base, which in turn is based on the Subscriptor
-  // class that asserts that this class cannot go out of scope while still in
-  // use in e.g. a preconditioner.
+  // class that asserts that this class is not accessed after going out of scope
+  // e.g. in a preconditioner.
   template <int dim, int fe_degree, typename number>
   LaplaceOperator<dim, fe_degree, number>::LaplaceOperator()
     : MatrixFreeOperators::Base<dim,
index 6f4bf6376cfe613fbbf6f742832769e38f082112..90dc8223f9f27e947b1dc75499a69b36706170b3 100644 (file)
@@ -116,55 +116,6 @@ from the optimal square.
 
 
 
-For completeness, we show what happens if the code we commented about
-in the destructor of the <code>Step6</code> class is omitted
-from this example:
-
-~~~{}
-An error occurred in line <104> of file <.../source/base/subscriptor.cc> in function
-    void dealii::Subscriptor::check_no_subscribers() const
-The violated condition was:
-    counter == 0
-Additional information:
-    Object of class N6dealii15SparsityPatternE is still used by 1 other objects.
-
-(Additional information:
-  from Subscriber SparseMatrix)
-
-See the entry in the Frequently Asked Questions of deal.II (linked to from
-http://www.dealii.org/) for a lot more information on what this error means and
-how to fix programs in which it happens.
-
-Stacktrace:
------------
-#0  .../lib/libdeal_II.g.so.9.0.0: dealii::Subscriptor::check_no_subscribers() const noexcept
-#   .../lib/libdeal_II.g.so.9.0.0: dealii::Subscriptor::~Subscriptor()
-#2  ./step-6: Step6<2>::~Step6()
-#3  ./step-6: main
---------------------------------------------------------
-~~~
-
-From the above error message, we conclude that something is still using an
-object with type <code>N6dealii15SparsityPatternE</code>. This is of course the <a
-href="http://en.wikipedia.org/wiki/Name_mangling">"mangled" name</a> for
-<code>SparsityPattern</code>. The mangling works as follows: the <code>N6</code> indicates
-a namespace with six characters (i.e., <code>dealii</code>) and the
-<code>15</code> indicates the number of characters of the template class (i.e.,
-<code>SparsityPattern</code>).
-
-From this we can already glean a little bit who is the culprit here, and who the
-victim: The one object that still uses the SparsityPattern is the
-SparseMatrix.
-
-The stacktrace gives an indication of where the problem happened. We
-see that the AssertNothrow macro was triggered in the
-destructor of the SparseMatrix class (which inherits from Subscriptor) that was called
-through a few more functions from the destructor of the
-<code>Step6</code> class, exactly where we have commented out
-the call to SparseMatrix::clear().
-
-
-
 <a name="extensions"></a>
 <h3>Possibilities for extensions</h3>
 
index aac02d826a70f09e0440de7336b34180d03917e0..b45caaf7bd4f15af8104c460f49c5401d6af5e99 100644 (file)
@@ -96,7 +96,6 @@ class Step6
 {
 public:
   Step6();
-  ~Step6();
 
   void run();
 
@@ -118,9 +117,6 @@ private:
   // conditions.
   AffineConstraints<double> constraints;
 
-  // The sparsity pattern and sparse matrix are deliberately declared in the
-  // opposite of the order used in step-2 through step-5 to demonstrate the
-  // primary use of the Subscriptor and SmartPointer classes.
   SparseMatrix<double> system_matrix;
   SparsityPattern      sparsity_pattern;
 
@@ -159,69 +155,6 @@ Step6<dim>::Step6()
 {}
 
 
-// @sect4{Step6::~Step6}
-
-// Here comes the added destructor of the class. Some objects in deal.II store
-// pointers to other objects: in particular a SparseMatrix stores a SmartPointer
-// pointing to the SparsityPattern with which it was initialized. This example
-// deliberately declares the SparseMatrix before the SparsityPattern to make
-// this dependency clearer. Of course we could have left this order unchanged,
-// but we would like to show what happens if the order is reversed since this
-// produces a rather nasty side-effect and results in an error which is
-// difficult to track down if one does not know what happens.
-//
-// What happens is the following: when we initialize a SparseMatrix,
-// the matrix stores a pointer to the provided SparsityPattern instead of
-// copying it.  Since this pointer is used until either another
-// SparsityPattern is attached or the SparseMatrix is destructed, it would be
-// unwise to allow the SparsityPattern to be destructed before the
-// SparseMatrix. To disallow this, the SparseMatrix increases a counter inside
-// the SparsityPattern which counts how many objects use it (this is what the
-// <code>Subscriptor</code>/<code>SmartPointer</code> class pair is used for,
-// in case you want something like this for your own programs; see step-7 for
-// a more complete discussion of this topic). If we try to destroy the object
-// while the counter is larger than zero then the program will either abort
-// (the default) or print an error message and continue: see the documentation
-// of AssertNothrow for more details. In either case the program contains a
-// bug and this facility will, hopefully, point out where.
-//
-// To be fair, such errors due to object dependencies are not particularly
-// popular among programmers using deal.II, since they only tell us that
-// something is wrong, namely that <i>some</i> other object is still using the
-// object that is presently being destroyed, but most of the time not
-// <em>which</em> object is still using it. It is therefore often rather
-// time-consuming to find out where the problem exactly is, although it is
-// then usually straightforward to remedy the situation. However, we believe
-// that the effort to find invalid pointers to objects that no longer exist is
-// less if the problem is detected once the pointer becomes invalid, rather
-// than when non-existent objects are actually accessed again, since then
-// usually only invalid data is accessed, but no error is immediately raised.
-//
-// Coming back to the present situation, if we did not write this destructor,
-// then the compiler would generate code that triggers exactly the behavior
-// described above. The reason is that member variables of the
-// <code>Step6</code> class are destroyed bottom-up (i.e., in reverse order of
-// their declaration in the class), as always in C++. Thus, the
-// SparsityPattern will be destroyed before the SparseMatrix, since its
-// declaration is below the declaration of the sparsity pattern. This triggers
-// the situation above, and without manual intervention the program will abort
-// when the SparsityPattern is destroyed. What needs to be done is to
-// tell the SparseMatrix to release its pointer to the SparsityPattern. Of
-// course, the SparseMatrix will only release its pointer if it really does
-// not need the SparsityPattern any more. For this purpose, the SparseMatrix
-// class has a function SparseMatrix::clear() which resets the object to its
-// default-constructed state by deleting all data and resetting its pointer to
-// the SparsityPattern to 0. After this, you can safely destruct the
-// SparsityPattern since its internal counter will be zero.
-//
-// We show the output of the other case (where we do not call
-// SparseMatrix::clear()) in the results section below.
-template <int dim>
-Step6<dim>::~Step6()
-{
-  system_matrix.clear();
-}
-
 
 // @sect4{Step6::setup_system}
 
index 7f6a93e521ff56e290733d0f133631bfe0c66426..ff958d9694dfc1e2442f3329f174b00718b58763 100644 (file)
@@ -362,27 +362,6 @@ namespace Step60
     ParameterAcceptorProxy<Functions::ParsedFunction<spacedim>>
       embedded_configuration_function;
 
-
-    // The embedded mapping. Notice that the order in which we construct these
-    // unique pointers is important. They will be destroyed in reversed order,
-    // so it is important that we respect the dependency tree. In particular,
-    // the embedded mapping will depend on both the `embedded_dh` and the
-    // `embedded_configuration`. If we declare it after the above two, we are
-    // fine, otherwise we would have do release this pointer manually in the
-    // destructor, or we'd get an error like
-    //
-    // @code
-    // --------------------------------------------------------
-    // An error occurred in line <104> of file <../source/base/subscriptor.cc>
-    // in function
-    //     void dealii::Subscriptor::check_no_subscribers() const
-    // The violated condition was:
-    //     counter == 0
-    // Additional information:
-    //     (none)
-    // @endcode
-    //
-    // at the end of the program.
     std::unique_ptr<Mapping<dim, spacedim>> embedded_mapping;
 
     // We do the same thing to specify the value of the function $g$,
index c8bf1412ab1ab11705cfff2ff13d0d9de462a6fb..97c0e7daa91c9180b20fe06dace41b38fa6f67ee 100644 (file)
@@ -373,16 +373,12 @@ namespace Step7
     // beginning of the program or an outer loop, and they are destroyed at
     // the very end. The question is: can we guarantee that the two objects
     // which the DoFHandler uses, live at least as long as they are in use?
-    // This means that the DoFHandler must have some kind of lock on the
-    // destruction of the other objects, and it can only release this lock
-    // once it has cleared all active references to these objects. We have
-    // seen what happens if we violate this order of destruction in the
-    // previous example program: an exception is thrown that terminates the
-    // program in order to notify the programmer of this potentially dangerous
-    // state where an object is pointed to that no longer persists.
+    // This means that the DoFHandler must have some kind of knowledge on the
+    // destruction of the other objects.
     //
     // We will show here how the library managed to find out that there are
-    // still active references to an object. Basically, the method is along
+    // still active references to an object and the object is still alive
+    // frome the point of view of a using object. Basically, the method is along
     // the following line: all objects that are subject to such potentially
     // dangerous pointers are derived from a class called Subscriptor. For
     // example, the Triangulation, DoFHandler, and a base class of the
@@ -392,40 +388,24 @@ namespace Step7
     // a pointer to that object, we can increase its use counter, and when we
     // move away our pointer or do not need it any more, we decrease the
     // counter again. This way, we can always check how many objects still use
-    // that object.
+    // that object. Additionally, the class requires to know about a pointer
+    // that it can use to tell the subscribing object about its invalidation.
     //
-    // On the other hand, if an object of a class that is derived from the
-    // Subscriptor class is destroyed, it also has to call the destructor of
-    // the Subscriptor class. In this destructor, there will then be a check
-    // whether the counter is really zero. If yes, then there are no active
-    // references to this object any more, and we can safely destroy it. If
-    // the counter is non-zero, however, then the destruction would result in
-    // stale and thus potentially dangerous pointers, and we rather throw an
-    // exception to alert the programmer that this is doing something
-    // dangerous and the program better be fixed.
+    // If an object of a class that is derived from the Subscriptor class is
+    // destroyed, it also has to call the destructor of the Subscriptor class.
+    // In this destructor, we tell all the subscribing objects about the
+    // invalidation of the object using the stored pointers. The same happens
+    // when the object appears on the right hand side of a move expression,
+    // i.e., it will no longer contain valid content after the operation. The
+    // subscribing class is expected to check the value stored in its
+    // corresponding pointer before trying to access the object subscribed to.
     //
-    // While this certainly all sounds very well, it has some problems in
-    // terms of usability: what happens if I forget to increase the counter
-    // when I let a pointer point to such an object? And what happens if I
-    // forget to decrease it again? Note that this may lead to extremely
-    // difficult to find bugs, since the place where we have forgotten
-    // something may be far away from the place where the check for zeroness
-    // of the counter upon destruction actually fails. This kind of bug is
-    // rather annoying and usually very hard to fix.
-    //
-    // The solution to this problem is to again use some C++ trickery: we
-    // create a class that acts just like a pointer, i.e. can be dereferenced,
-    // can be assigned to and from other pointers, and so on. This can be done
-    // by overloading the several dereferencing operators of that
-    // class. Within the constructors, destructors, and assignment operators
-    // of that class, we can however also manage increasing or decreasing the
-    // use counters of the objects we point to. Objects of that class
-    // therefore can be used just like ordinary pointers to objects, but they
-    // also serve to change the use counters of those objects without the need
-    // for the programmer to do so herself. The class that actually does all
-    // this is called SmartPointer and takes as template parameter the data
-    // type of the object which it shall point to. The latter type may be any
-    // class, as long as it is derived from the Subscriptor class.
+    // This is exactly what the SmartPointer class is doing. It basically acts
+    // just like a pointer, i.e. it can be dereferenced, can be assigned to and
+    // from other pointers, and so on. On top of that it uses the mechanism
+    // described above to find out if the pointer this class is representing is
+    // dangling when we try to dereference it. In that case an exception is
+    // thrown.
     //
     // In the present example program, we want to protect the finite element
     // object from the situation that for some reason the finite element

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.