User:Andrecaldas/SharedPtr

= Introduction: sharing document objects all over! = Each FreeCAD document (App::Document) is composed of document objects (DocumentObject). Each document object should be an autonomous structure that is functional even if it is not inserted in a document. It's behaviour cannot be unexpected just because it is not attached to a document.

Many functions deal with document objects. They are usually passed a pointer to the object. But the more FreeCAD becomes multithreaded, the more we need warranties that a certain object is still functional, even if while processing takes place, the object is concurrently removed from the document, for example.

Today, assuring that a certain object will not be destroyed or removed from the document it belongs to is a complicated task that relies on verifying its internal status (usually in thread-unsafe ways). For example:
 * Afraid of removing an object because of its status.
 * Others need to know that an object is about to be deleted.
 * Variable pcNameInDocument is set to nullptr to indicate some internal status change.
 * Document object is destructed only if it does not belong to a document.
 * Freezes main thread, waiting for parallel processing to finish. The result of the processing will obviously be discarded, though. :-(

Things can become much simpler and much safer if we use shared_ptr. As long as someone is holding a shared_ptr to an object, the object is (almost) guaranteed to be valid. If we design things in such a way that the document is functional even without belonging to a document, everything shall simply work without giving unexpected results (crash).

An example of something that would crash if we were a little more multithreaded: accessing a Document that might not be available anymore.

Pointer management
Instances of Document and DocumentObject should be managed by shared_ptr.

Data inside a DocumentObject can be:
 * Part of the object;
 * Managed by unique_ptr; or
 * Managed by shared_ptr.

Downward reference
Downward reference means ownership. The owning object shall hold a unique_ptr or shared_ptr to the downward reference.

Preferably, once set, those smart pointers should not change. This way, functions that are called in a context where a valid unique_ptr or shared_ptr are assured to exist during the function call can take raw pointers. In particular, functions called by methods in the class that owns a resource can take raw pointers.

Upward reference
Since objects can be detached from their owners in threads different from ours, upward references should be weak_ptr.

Methods that use this weak_ptr shall deal gracefully with weak_ptr::lock returning an invalid lock (shared_ptr). Those methods can either:
 * 1) Return a reasonable default (preferable); or
 * 2) Throw an exception.

Arbitrary reference
To reference an arbitrary object, FreeCAD uses an infrastructure named ObjectIdentifier. I do not understand much about ObjectIdentifier. But I propose a different solution I call "Accessor".

Source code: https://github.com/andre-caldas/FreeCAD/tree/NamedSketcher/src/Base/Accessor

More information: Accessor infrastructure.

A sequence of string identifies an object. Actually, it identifies some data exported by an object. It can be an integer, a string, another object... anything. When the path is resolved, a shared_ptr to the resource is returned. This shared_ptr is not supposed to be stored. It is supposed to be used locally by some function. While the returned shared_ptr exists, the accessed resource is guaranteed to exist. Because of multithreading, It might be removed from the document. It might not satisfy the path anymore... but it is assured to exist.

It is not implemented, yet... but the Accessor can have a URI that can be used to refer to an object in a different document.

Each object shall have a UUID and an optional name that can be set by the user. The UUID is assigned when the object is first created and is stored and restored when the object is serialized (saved to a file) and unserialized.

Python
Python objects are implemented by a "trampoline" class. Those shall use a shared_ptr to hold a reference to the original object. This way, the C++ counter part does not need to care about python reference counter.

Using weak_ptr in python seems not viable. Therefore, care should be taken in a way such that the shared_ptr (probably returned by the Accessor reference to some object) is not stored, but only used locally.

Advantages
TODO: list to pages with further explanation.
 * Automatic signal/slot disconnection.
 * True thread safety.
 * Non blocking parallel processing.
 * Enables the use of asynchronous signals.
 * The GUI thread can become very light-weight... making it more responsive.