From: David Wells Date: Sat, 28 Apr 2018 21:37:34 +0000 (-0400) Subject: Redo the SmartPointer description in step-6. X-Git-Tag: v9.0.0-rc1~53^2~5 X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d78f930db6e392ca08b637eb2c7183e61663c253;p=dealii.git Redo the SmartPointer description in step-6. The old version doesn't make sense anymore since a Triangulation does not store a SmartPointer pointing to a manifold. --- diff --git a/examples/step-6/doc/results.dox b/examples/step-6/doc/results.dox index cdc3245d56..5338b1542c 100644 --- a/examples/step-6/doc/results.dox +++ b/examples/step-6/doc/results.dox @@ -113,16 +113,16 @@ For completeness, we show what happens if the code we commented about in the destructor of the Step6 class is omitted from this example. -@code --------------------------------------------------------- -An error occurred in line <104> of file in function +~~~{} +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 N6dealii17SphericalManifoldILi2ELi2EEE is still used by 1 other objects. + Object of class N6dealii15SparsityPatternE is still used by 1 other objects. -(Additional information: ) +(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 @@ -130,32 +130,31 @@ how to fix programs in which it happens. Stacktrace: ----------- -#0 /lib/libdeal_II.g.so: dealii::Subscriptor::check_no_subscribers() const -#1 /lib/libdeal_II.g.so: dealii::Subscriptor::~Subscriptor() -#2 /lib/libdeal_II.g.so: dealii::Manifold<2, 2>::~Manifold() -#3 ./step-6: Step6<2>::~Step6() -#4 ./step-6: main +#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 -------------------------------------------------------- -@endcode +~~~ From the above error message, we conclude that something is still using an -object with type N6dealii4FE_QILi2EE. This is of course the N6dealii15SparsityPatternE. This is of course the "mangled" name for -FE_Q. The mangling works as follows: the N6 indicates +SparsityPattern. The mangling works as follows: the N6 indicates a namespace with six characters (i.e., dealii) and the -4 indicates the number of characters of the template class (i.e., -FE_Q). +15 indicates the number of characters of the template class (i.e., +SparsityPattern). -The rest of the text is then template arguments. From this we can already glean -a little bit who's the culprit here, and who the victim: The one object that -still uses the finite element is the dof_handler object. +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 exception was triggered in the -destructor of the FiniteElement class that was called +destructor of the SparseMatrix class (which inherits from Subscriptor) that was called through a few more functions from the destructor of the Step6 class, exactly where we have commented out -the call to DoFHandler::clear(). +the call to SparseMatrix::clear(). diff --git a/examples/step-6/step-6.cc b/examples/step-6/step-6.cc index 543bcfc15b..186c3f4882 100644 --- a/examples/step-6/step-6.cc +++ b/examples/step-6/step-6.cc @@ -120,8 +120,11 @@ private: // conditions. ConstraintMatrix constraints; - SparsityPattern sparsity_pattern; + // 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 + // main use of Subscriptor and SmartPointer. SparseMatrix system_matrix; + SparsityPattern sparsity_pattern; Vector solution; Vector system_rhs; @@ -161,61 +164,57 @@ Step6::Step6 () // @sect4{Step6::~Step6} -// Here comes the added destructor of the class. The reason why we want to add -// it is to pick up an issue we already started to discuss in step-1: -// the boundary object was defined before and not after the -// triangulation object. 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. +// 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. // -// Basically what happens is the following: when we set a manifold description -// to the triangulation using the function call -// triangulation.set_manifold (0, boundary), the -// Triangulation object also stores a pointer to the -// Manifold object in use. Since this pointer is used until either -// another Manifold object is set as boundary description or until -// the Triangulation object is destroyed, it would be unwise if we -// would allow the boundary to be deleted before the -// triangulation. To disallow this, the triangulation -// increases a counter inside the boundary which counts how many -// objects use it (this is what the +// Basically 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 // Subscriptor/SmartPointer 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). The boundary will -// refuse its destruction if that counter is larger than zero, since then some -// other objects might rely on its persistence. An exception will then be -// thrown and the program will usually abort upon the attempt to destroy -// boundary. +// a more complete discussion of this topic). If 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 exceptions about still used objects are not particularly +// 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 some other object is still using the object -// that is presently being destroyed, but most of the time not which 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 -// references to objects that do no longer exist is less if the problem is -// detected once the reference 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. +// something is wrong, namely that some other object is still using the +// object that is presently being destroyed, but most of the time not +// which 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, // the compiler will generate code that triggers exactly the behavior sketched // above. The reason is that member variables of the Step6 class // are destroyed bottom-up (i.e., in reverse order of their declaration in the -// class), as always in C++. Thus, the boundary object will be -// destroyed before the triangulation object, since its declaration is below -// the one of the triangulation. This triggers the situation above, and an -// exception will be raised when the boundary object is -// destroyed. What needs to be done is to tell the triangulation -// object to release its lock to boundary. Of course, the -// triangulation will only release its lock if it really does not -// need the boundary any more. For this purpose, the -// Triangulation class has a function clear which -// resets the object into a virgin state by deleting all data and releases its -// lock to the finite element. After this, you can safely destruct the -// boundary object since its internal counter is then zero. +// class), as always in C++. Thus, the SparsityPattern will be destroyed +// before the SparseMatrix, since its declaration is below the one of the +// sparsity pattern. This triggers the situation above, and an exception will +// be raised 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. // // For completeness, we add the output of the exception that would have been // triggered without this destructor, to the end of the results section of @@ -223,7 +222,7 @@ Step6::Step6 () template Step6::~Step6 () { - triangulation.clear (); + system_matrix.clear(); }