// 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
// 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,
-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>
{
public:
Step6();
- ~Step6();
void run();
// 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;
{}
-// @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}
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$,
// 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
// 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