CXSafePointer

From cxwiki

CXSafePointer is a templated class which acts equivalently to a C pointer but which automatically becomes nullptr if the referenced object is deleted. The referenced object must derived from CXSafePointerTarget.

CXSafePointer<T> is designed to act in a manner syntactically equivalent to T* where possible.

Restrictions

CXSafePointer objects have limited internal type safety. This amounts to them being roughly equivalent to a C pointer:

  • They may be assigned safely from multiple threads if no specific ordering guarantees are required.
  • They can be queried safely from any thread.
    • However the referenced object could be destroyed on another thread after you query it, but before you try to use it.
  • It is considered an error to create a CXSafePointer reference to an object which is already undergoing destruction.
  • Taking a CXSafePointer reference to an object, on any thread, is fine as long as you have ownership of the object at the time.
  • Detaching CXSafePointer references from a CXSafePointerTarget is fine, even if those references are held by another thread, as long as the other thread is not making blind assumptions about their contents.
    • For example, this is bad:if (myPtr) myPtr->CallSomething();
    • This is fine as long as the object isnt actually being destroyed: if (MyObj* obj = myPtr) obj->CallSomething();
    • If you actually destroy the underlying object, then there are no thread-safety guarantes. The CXSafePointer will become null, but the other threads may be in the process of executing methods on the object (etc.)

In practice, the limited thread-safety guarantees mean that it is hard to use CXSafePointer across threads. Where they come in useful is the following scenario:

  • Thread A creates an object, retains a reference.
  • Thread A creates a CXSafePointer and passes it to thread B.
    • Note that it is safe for thread B to hold the smart pointer, but not safe for thread B to access through the smart pointer.
  • Thread A optionally destroys the object.
  • Thread B passes the CXSafePointer back to thread A.
  • Thread A can safely test whether the object still exists and can safely access it if does.

This scenario is fairly common for certain types of lambda:

// We are on the main thread.
CXSafePointer<MyObj> obj = ...;
CXWorkerHost::EnqueueTask(nullptr, [obj] {
    auto data = DoSomething(); // note that we can't use 'obj' safely here.
    CXWorkerHost::EnqueueTaskOnMainThread(nullptr, [obj, data] {
        if (obj)
          obj->NotifyCompletion(data);
      });
  });