DEAL_II_NAMESPACE_OPEN
/**
- * Smart pointers avoid destruction of an object in use. They can be used just
+ * Smart pointers avoid using dangling pointers. They can be used just
* like a pointer (i.e. using the <tt>*</tt> and <tt>-></tt> operators and
- * through casting) but make sure that the object pointed to is not deleted in
- * the course of use of the pointer by signaling the pointee its use.
+ * through casting) but make sure that the object pointed to is not deleted or
+ * moved from in the course of use of the pointer by signaling the pointee its
+ * use.
*
* Objects pointed to, i.e. the class T, should inherit Subscriptor or must
* implement the same functionality. Null pointers are an exception from this
SmartPointer(const SmartPointer<T, P> &tt);
/**
- * Constructor taking a normal pointer. If possible, i.e. if the pointer is
+ * Constructor taking a normal pointer. If possible, i.e. if the pointer is
* not a null pointer, the constructor subscribes to the given object to
* lock it, i.e. to prevent its destruction before the end of its use.
*
SmartPointer(T *t, const char *id);
/**
- * Constructor taking a normal pointer. If possible, i.e. if the pointer is
+ * Constructor taking a normal pointer. If possible, i.e. if the pointer is
* not a null pointer, the constructor subscribes to the given object to
* lock it, i.e. to prevent its destruction before the end of its use. The
* id of this pointer is set to the name of the class P.
*/
SmartPointer(T *t);
-
/**
* Destructor, removing the subscription.
*/
operator=(T *tt);
/**
- * Assignment operator for SmartPointer. The pointer subscribes to the new
+ * Assignment operator for SmartPointer. The pointer subscribes to the new
* object automatically and unsubscribes to an old one if it exists.
*/
template <class Q>
operator=(const SmartPointer<T, Q> &tt);
/**
- * Assignment operator for SmartPointer. The pointer subscribes to the new
+ * Assignment operator for SmartPointer. The pointer subscribes to the new
* object automatically and unsubscribes to an old one if it exists.
*/
SmartPointer<T, P> &
operator T *() const;
/**
- * Dereferencing operator. This operator throws an ExcNotInitialized if the
+ * Dereferencing operator. This operator throws an ExcNotInitialized() if the
* pointer is a null pointer.
*/
T &operator*() const;
/**
- * Dereferencing operator. This operator throws an ExcNotInitialized if the
+ * Dereferencing operator. This operator throws an ExcNotInitializedi() if the
* pointer is a null pointer.
*/
T *operator->() const;
* The identification for the subscriptor.
*/
const char *const id;
+
+ /**
+ * The Smartpointer is invalidated when the object pointed to is destroyed
+ * or moved from.
+ */
+ bool pointed_to_object_is_alive;
};
inline SmartPointer<T, P>::SmartPointer()
: t(nullptr)
, id(typeid(P).name())
+ , pointed_to_object_is_alive(false)
{}
inline SmartPointer<T, P>::SmartPointer(T *t)
: t(t)
, id(typeid(P).name())
+ , pointed_to_object_is_alive(false)
{
if (t != nullptr)
- t->subscribe(id);
+ t->subscribe(&pointed_to_object_is_alive, id);
}
inline SmartPointer<T, P>::SmartPointer(T *t, const char *id)
: t(t)
, id(id)
+ , pointed_to_object_is_alive(false)
{
if (t != nullptr)
- t->subscribe(id);
+ t->subscribe(&pointed_to_object_is_alive, id);
}
inline SmartPointer<T, P>::SmartPointer(const SmartPointer<T, Q> &tt)
: t(tt.t)
, id(tt.id)
+ , pointed_to_object_is_alive(false)
{
- if (t != nullptr)
- t->subscribe(id);
+ if (tt.pointed_to_object_is_alive && t != nullptr)
+ t->subscribe(&pointed_to_object_is_alive, id);
}
inline SmartPointer<T, P>::SmartPointer(const SmartPointer<T, P> &tt)
: t(tt.t)
, id(tt.id)
+ , pointed_to_object_is_alive(false)
{
- if (t != nullptr)
- t->subscribe(id);
+ if (tt.pointed_to_object_is_alive && t != nullptr)
+ t->subscribe(&pointed_to_object_is_alive, id);
}
template <typename T, typename P>
inline SmartPointer<T, P>::~SmartPointer()
{
- if (t != nullptr)
- t->unsubscribe(id);
+ if (pointed_to_object_is_alive && t != nullptr)
+ t->unsubscribe(&pointed_to_object_is_alive, id);
}
inline void
SmartPointer<T, P>::clear()
{
- if (t != nullptr)
+ if (pointed_to_object_is_alive && t != nullptr)
{
- t->unsubscribe(id);
+ t->unsubscribe(&pointed_to_object_is_alive, id);
delete t;
- t = nullptr;
+ Assert(pointed_to_object_is_alive == false, ExcInternalError());
}
+ t = nullptr;
}
if (t == tt)
return *this;
- if (t != nullptr)
- t->unsubscribe(id);
+ if (pointed_to_object_is_alive && t != nullptr)
+ t->unsubscribe(&pointed_to_object_is_alive, id);
t = tt;
if (tt != nullptr)
- tt->subscribe(id);
+ tt->subscribe(&pointed_to_object_is_alive, id);
return *this;
}
if (&tt == this)
return *this;
- if (t != nullptr)
- t->unsubscribe(id);
+ if (pointed_to_object_is_alive && t != nullptr)
+ t->unsubscribe(&pointed_to_object_is_alive, id);
t = static_cast<T *>(tt);
- if (tt != nullptr)
- tt->subscribe(id);
+ if (tt.pointed_to_object_is_alive && tt != nullptr)
+ tt->subscribe(&pointed_to_object_is_alive, id);
return *this;
}
if (&tt == this)
return *this;
- if (t != nullptr)
- t->unsubscribe(id);
+ if (pointed_to_object_is_alive && t != nullptr)
+ t->unsubscribe(&pointed_to_object_is_alive, id);
t = static_cast<T *>(tt);
- if (tt != nullptr)
- tt->subscribe(id);
+ if (tt.pointed_to_object_is_alive && tt != nullptr)
+ tt->subscribe(&pointed_to_object_is_alive, id);
return *this;
}
inline T &SmartPointer<T, P>::operator*() const
{
Assert(t != nullptr, ExcNotInitialized());
+ Assert(pointed_to_object_is_alive,
+ ExcMessage("The object pointed to is not valid anymore."));
return *t;
}
inline T *SmartPointer<T, P>::operator->() const
{
Assert(t != nullptr, ExcNotInitialized());
+ Assert(pointed_to_object_is_alive,
+ ExcMessage("The object pointed to is not valid anymore."));
return t;
}
inline void
SmartPointer<T, P>::swap(T *&tt)
{
- if (t != nullptr)
- t->unsubscribe(id);
+ if (pointed_to_object_is_alive && t != nullptr)
+ t->unsubscribe(pointed_to_object_is_alive, id);
std::swap(t, tt);
if (t != nullptr)
- t->subscribe(id);
+ t->subscribe(pointed_to_object_is_alive, id);
}
#include <mutex>
#include <string>
#include <typeinfo>
+#include <vector>
DEAL_II_NAMESPACE_OPEN
* Handling of subscriptions.
*
* This class, as a base class, allows to keep track of other objects using a
- * specific object. It is used, when an object, given to a constructor by
- * reference, is stored. Then, the original object may not be deleted before
- * the dependent object is deleted. You can assert this constraint by letting
- * the object passed be derived from this class and let the user subscribe()
- * to this object. The destructor the used object inherits from the
- * Subscriptor class then will lead to an error when destruction is attempted
- * while there are still subscriptions.
+ * specific object. It is used to avoid that pointers that point to an object of
+ * a class derived from Subscriptor are referenced after that object has been
+ * invalidated. Here, invalidation is assumend to happen when the object is
+ * moved from or destroyed.
+ * The mechanism works as follows: The member function subscribe() accepts a
+ * pointer to a boolean that is modified on invalidation. The object that owns
+ * this pointer (usually an object of class type SmartPointer) is then expected
+ * to check the state of the boolean before trying to access this class.
*
* The utility of this class is even enhanced by providing identifying strings
- * to the functions subscribe() and unsubscribe(). In case of a hanging
- * subscription during destruction, this string will be listed in the
- * exception's message. These strings are represented as <code>const char
- * *</code> pointers since the underlying buffer comes from (and is managed
- * by) the run-time type information system: more exactly, these pointers are
- * the result the function call <code>typeid(x).name()</code> where
+ * to the functions subscribe() and unsubscribe(). These strings are represented
+ * as <code>const char</code> pointers since the underlying buffer comes from
+ * (and is managed by) the run-time type information system: more exactly, these
+ * pointers are the result the function call <code>typeid(x).name()</code> where
* <code>x</code> is some object. Therefore, the pointers provided to
* subscribe() and to unsubscribe() must be the same. Strings with equal
* contents will not be recognized to be the same. The handling in
* SmartPointer will take care of this.
+ * The current subscribers to this class can be obtained by calling
+ * list_subscribers().
*
* @ingroup memory
- * @author Guido Kanschat, 1998 - 2005
+ * @author Guido Kanschat, Daniel Arndt, 1998 - 2005, 2018
*/
class Subscriptor
{
operator=(const Subscriptor &);
/**
- * Move assignment operator.
- *
- * Asserts that the counter for the moved object is zero.
+ * Move assignment operator. Only invalidates the object moved from.
*/
Subscriptor &
operator=(Subscriptor &&) noexcept;
/**
- * Subscribes a user of the object. The subscriber may be identified by text
- * supplied as <tt>identifier</tt>.
+ * Subscribes a user of the object by storing the pointer @p validity. The
+ * subscriber may be identified by text supplied as @p identifier.
*/
void
- subscribe(const char *identifier = nullptr) const;
+ subscribe(bool *const validity, const char *identifier = nullptr) const;
/**
* Unsubscribes a user from the object.
*
- * @note The <tt>identifier</tt> must be the <b>same pointer</b> as the one
- * supplied to subscribe(), not just the same text.
+ * @note The @p identifier and the @p validity pointer must be the same as
+ * the one supplied to subscribe().
*/
void
- unsubscribe(const char *identifier = nullptr) const;
+ unsubscribe(bool *const validity, const char *identifier = nullptr) const;
/**
* Return the present number of subscriptions to this object. This allows to
*/
mutable std::map<const char *, unsigned int> counter_map;
+ /**
+ * In this vector, we store pointers to the validity bool in the SmartPointer
+ * objects that subscribe to this class.
+ */
+ mutable std::vector<bool *> validity_pointers;
+
/**
* Pointer to the typeinfo object of this object, from which we can later
* deduce the class name. Since this information on the derived class is
: counter(0)
, object_info(subscriptor.object_info)
{
- subscriptor.check_no_subscribers();
+ for (auto validity_ptr : subscriptor.validity_pointers)
+ *validity_ptr = false;
}
Subscriptor::~Subscriptor()
{
- check_no_subscribers();
+ for (auto validity_ptr : validity_pointers)
+ *validity_ptr = false;
object_info = nullptr;
}
Subscriptor &
Subscriptor::operator=(const Subscriptor &s)
{
- check_no_subscribers();
object_info = s.object_info;
return *this;
}
Subscriptor &
Subscriptor::operator=(Subscriptor &&s) noexcept
{
- check_no_subscribers();
- s.check_no_subscribers();
+ for (auto validity_ptr : s.validity_pointers)
+ *validity_ptr = false;
object_info = s.object_info;
return *this;
}
void
-Subscriptor::subscribe(const char *id) const
+Subscriptor::subscribe(bool *const validity, const char *id) const
{
std::lock_guard<std::mutex> lock(mutex);
map_iterator it = counter_map.find(name);
if (it == counter_map.end())
counter_map.insert(map_value_type(name, 1U));
-
else
it->second++;
+
+ *validity = true;
+ validity_pointers.push_back(validity);
}
void
-Subscriptor::unsubscribe(const char *id) const
+Subscriptor::unsubscribe(bool *const validity, const char *id) const
{
const char *name = (id != nullptr) ? id : unknown_subscriber;
AssertNothrow(counter > 0, ExcNoSubscriber(object_info->name(), name));
--counter;
it->second--;
}
+
+ auto validity_ptr_it =
+ std::find(validity_pointers.begin(), validity_pointers.end(), validity);
+ if (validity_ptr_it == validity_pointers.end())
+ {
+ AssertNothrow(
+ validity_ptr_it != validity_pointers.end(),
+ ExcMessage(
+ "This Subscriptor object does not know anything about the supplied pointer!"));
+ }
+ else
+ validity_pointers.erase(validity_ptr_it);
}
-// check that Subscriptor objects need to be empty before assigning.
+// Check the behavior of the SmartPointer-Subscriptor pair
+// for copy and move semantics.
#include <deal.II/base/smartpointer.h>
initlog();
- // should work
{
- Subscriptor subscriptor_1;
- Subscriptor subscriptor_2;
+ deallog << "Checking copy assignment" << std::endl;
- SmartPointer<Subscriptor> smart(&subscriptor_1);
+ Subscriptor subscriptor_1;
+ Subscriptor subscriptor_2;
+ SmartPointer<Subscriptor> smart_pointer_1(&subscriptor_1);
+ SmartPointer<Subscriptor> smart_pointer_2(&subscriptor_2);
subscriptor_2 = subscriptor_1;
+
+ deallog << "Checking smart_pointer_1" << std::endl;
+ try
+ {
+ const auto dummy_1 = *smart_pointer_1;
+ (void)dummy_1;
+ }
+ catch (ExceptionBase &e)
+ {
+ deallog << e.get_exc_name() << std::endl;
+ }
+
+ deallog << "Checking smart_pointer_2" << std::endl;
+ try
+ {
+ const auto dummy_2 = *smart_pointer_2;
+ (void)dummy_2;
+ }
+ catch (ExceptionBase &e)
+ {
+ deallog << e.get_exc_name() << std::endl;
+ }
+ deallog << std::endl;
}
- try
- {
- Subscriptor subscriptor_1;
- Subscriptor subscriptor_2;
-
- SmartPointer<Subscriptor> smart(&subscriptor_2);
-
- subscriptor_2 = subscriptor_1;
- }
- catch (ExceptionBase &e)
- {
- deallog << e.get_exc_name() << std::endl;
- }
-
- try
- {
- Subscriptor subscriptor_1;
- Subscriptor subscriptor_2;
-
- SmartPointer<Subscriptor> smart(&subscriptor_1);
-
- subscriptor_2 = std::move(subscriptor_1);
- }
- catch (ExceptionBase &e)
- {
- deallog << e.get_exc_name() << std::endl;
- }
-
- try
- {
- Subscriptor subscriptor_1;
- Subscriptor subscriptor_2;
-
- SmartPointer<Subscriptor> smart(&subscriptor_2);
-
- subscriptor_2 = std::move(subscriptor_1);
- }
- catch (ExceptionBase &e)
- {
- deallog << e.get_exc_name() << std::endl;
- }
+ {
+ deallog << "Checking copy construction" << std::endl;
+
+ Subscriptor subscriptor_1;
+ SmartPointer<Subscriptor> smart_pointer_1(&subscriptor_1);
+
+ Subscriptor subscriptor_2(subscriptor_1);
+
+ deallog << "Checking smart_pointer_1" << std::endl;
+ try
+ {
+ const auto dummy_1 = *smart_pointer_1;
+ (void)dummy_1;
+ }
+ catch (ExceptionBase &e)
+ {
+ deallog << e.get_exc_name() << std::endl;
+ }
+ deallog << std::endl;
+ }
+
+ {
+ deallog << "Checking move assignement" << std::endl;
+
+ Subscriptor subscriptor_1;
+ Subscriptor subscriptor_2;
+ SmartPointer<Subscriptor> smart_pointer_1(&subscriptor_1);
+ SmartPointer<Subscriptor> smart_pointer_2(&subscriptor_2);
+
+ subscriptor_2 = std::move(subscriptor_1);
+
+ deallog << "Checking smart_pointer_1" << std::endl;
+ try
+ {
+ const auto dummy_1 = *smart_pointer_1;
+ (void)dummy_1;
+ }
+ catch (ExceptionBase &e)
+ {
+ deallog << e.get_exc_name() << std::endl;
+ }
+
+ deallog << "Checking smart_pointer_2" << std::endl;
+ try
+ {
+ const auto dummy_2 = *smart_pointer_2;
+ (void)dummy_2;
+ }
+ catch (ExceptionBase &e)
+ {
+ deallog << e.get_exc_name() << std::endl;
+ }
+ deallog << std::endl;
+ }
+
+ {
+ deallog << "Checking move construction" << std::endl;
+
+ Subscriptor subscriptor_1;
+ SmartPointer<Subscriptor> smart_pointer_1(&subscriptor_1);
+
+ Subscriptor subscriptor_2(std::move(subscriptor_1));
+
+ deallog << "Checking smart_pointer_1" << std::endl;
+ try
+ {
+ const auto dummy_1 = *smart_pointer_1;
+ (void)dummy_1;
+ }
+ catch (ExceptionBase &e)
+ {
+ deallog << e.get_exc_name() << std::endl;
+ }
+ }
}
-DEAL::Exception: ExcInUse (counter.load(), object_info->name(), infostring)
+DEAL::Checking copy assignment
+DEAL::Checking smart_pointer_1
+DEAL::Checking smart_pointer_2
DEAL::
---------------------------------------------------------
-An error occurred in file <subscriptor.cc> in function
- void dealii::Subscriptor::check_no_subscribers() const
-The violated condition was:
- counter == 0
-Additional information:
- Object of class N6dealii11SubscriptorE is still used by 1 other objects.
-
-(Additional information:
- from Subscriber v)
-
-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.
---------------------------------------------------------
-
-DEAL::Exception: ExcInUse (counter.load(), object_info->name(), infostring)
+DEAL::Checking copy construction
+DEAL::Checking smart_pointer_1
DEAL::
---------------------------------------------------------
-An error occurred in file <subscriptor.cc> in function
- void dealii::Subscriptor::check_no_subscribers() const
-The violated condition was:
- counter == 0
-Additional information:
- Object of class N6dealii11SubscriptorE is still used by 1 other objects.
-
-(Additional information:
- from Subscriber v)
-
-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.
---------------------------------------------------------
-
-DEAL::Exception: ExcInUse (counter.load(), object_info->name(), infostring)
+DEAL::Checking move assignement
+DEAL::Checking smart_pointer_1
+DEAL::ExcMessage("The object pointed to is not valid anymore.")
+DEAL::Checking smart_pointer_2
DEAL::
---------------------------------------------------------
-An error occurred in file <subscriptor.cc> in function
- void dealii::Subscriptor::check_no_subscribers() const
-The violated condition was:
- counter == 0
-Additional information:
- Object of class N6dealii11SubscriptorE is still used by 1 other objects.
-
-(Additional information:
- from Subscriber v)
-
-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.
---------------------------------------------------------
-
+DEAL::Checking move construction
+DEAL::Checking smart_pointer_1
+DEAL::ExcMessage("The object pointed to is not valid anymore.")
};
-// A sentinel that stores a reference to a SmartPointer and resets the
-// Smartpointer in its destructor.
-
-template <typename T, typename P>
-class Sentinel
-{
-public:
- Sentinel(SmartPointer<T, P> &smart_pointer)
- : smart_pointer_(smart_pointer)
- {}
-
- ~Sentinel()
- {
- // This assumes that the first object stored in SmartPointer is a raw
- // pointer T*
- *reinterpret_cast<T **>(&smart_pointer_) = nullptr;
- }
-
-private:
- SmartPointer<T, P> &smart_pointer_;
-};
-
int
main()
Test c("C");
r = &c;
- // We have to dance a happy little dance here:
- //
- // In our case the object D defined below will go out of scope before the
- // smartpointer "Test R" does. Under normal circumstances this triggers
- // an ExcNotUsed and aborts the program. BUT, this is a unit test and so,
- // aborting on exception is disabled and we continue execution.
- // Unfortunately, this triggers a "use after scope" memory access error
- // when finally "Test R" goes out of scope and tries to unsubscribe from
- // D that already got destroyed.
- //
- // Work around this issue by creating a sentinel that gets destroyed
- // after D but before "Test R" that simply resets the SmartPointer.
- Sentinel<Test, Test> sentinel(r);
-
- Test d("D");
- r = &d;
+ // Test that a dangling pointer is correctly detected.
+ try
+ {
+ {
+ Test d("D");
+ r = &d;
+ }
+ const auto dummy = *r;
+ }
+ catch (const ExceptionBase &exc)
+ {
+ deallog << exc.get_exc_name() << std::endl;
+ }
}
DEAL::Construct C
DEAL::Construct D
DEAL::Destruct D
-DEAL::Exception: ExcInUse (counter.load(), object_info->name(), infostring)
-DEAL::
---------------------------------------------------------
-An error occurred in file <subscriptor.cc> in function
- void dealii::Subscriptor::check_no_subscribers() const
-The violated condition was:
- counter == 0
-Additional information:
- Object of class 4Test is still used by 1 other objects.
-
-(Additional information:
- from Subscriber Test R)
-
-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.
---------------------------------------------------------
-
+DEAL::ExcMessage("The object pointed to is not valid anymore.")
DEAL::Destruct C
DEAL::Destruct B
DEAL::Destruct A
initlog();
Subscriptor subscriptor;
- subscriptor.subscribe("a");
- subscriptor.unsubscribe("b");
- subscriptor.unsubscribe("a");
+ bool dummy_a;
+ subscriptor.subscribe(&dummy_a, "a");
+ subscriptor.unsubscribe(&dummy_a, "b");
+ subscriptor.unsubscribe(&dummy_a, "a");
return 0;
}
DEAL::
--------------------------------------------------------
An error occurred in file <subscriptor.cc> in function
- void dealii::Subscriptor::unsubscribe(const char*) const
+ void dealii::Subscriptor::unsubscribe(bool*, const char*) const
The violated condition was:
it != counter_map.end()
Additional information:
No subscriber with identifier <b> subscribes to this object of class N6dealii11SubscriptorE. Consequently, it cannot be unsubscribed.
--------------------------------------------------------
+DEAL::Exception: ExcMessage( "This Subscriptor object does not know anything about the supplied pointer!")
+DEAL::
+--------------------------------------------------------
+An error occurred in file <subscriptor.cc> in function
+ void dealii::Subscriptor::unsubscribe(bool*, const char*) const
+The violated condition was:
+ validity_ptr_it != validity_pointers.end()
+Additional information:
+ This Subscriptor object does not know anything about the supplied pointer!
+--------------------------------------------------------
+