Storage Class
template <typename StorageStruct> class Tasking::StorageA class template for custom data exchange in the running task tree. More...
Header: | #include <solutions/tasking/tasktree.h> |
Inherits: | Tasking::StorageBase |
Note: All functions in this class are reentrant.
Public Functions
Storage() | |
StorageStruct * | activeStorage() const |
StorageStruct & | operator*() const |
StorageStruct * | operator->() const |
Detailed Description
The Storage class template is responsible for dynamically creating and destructing objects of the custom StorageStruct
type. The creation and destruction are managed by the running task tree. If a Storage object is placed inside a Group element, the running task tree creates the StorageStruct
object when the group is started and before the group's setup handler is called. Later, whenever any handler inside this group is called, the task tree activates the previously created instance of the StorageStruct
object. This includes all tasks' and groups' setup and done handlers inside the group where the Storage object was placed, also within the nested groups. When a copy of the Storage object is passed to the handler via the lambda capture, the handler may access the instance activated by the running task tree via the operator->(), operator*(), or activeStorage() method. If two handlers capture the same Storage object, one of them may store a custom data there, and the other may read it afterwards. When the group is finished, the previously created instance of the StorageStruct
object is destroyed after the group's done handler is called.
An example of data exchange between tasks:
const Storage<QString> storage; const auto onFirstDone = [storage](const Task &task) { // Assings QString, taken from the first task result, to the active QString instance // of the Storage object. *storage = task.getResultAsString(); }; const auto onSecondSetup = [storage](Task &task) { // Reads QString from the active QString instance of the Storage object and use it to // configure the second task before start. task.configureWithString(*storage); }; const Group root { // The running task tree creates QString instance when root in entered storage, // The done handler of the first task stores the QString in the storage TaskItem(..., onFirstDone), // The setup handler of the second task reads the QString from the storage TaskItem(onSecondSetup, ...) };
Since the root group executes its tasks sequentially, the onFirstDone
handler is always called before the onSecondSetup
handler. This means that the QString data, read from the storage
inside the onSecondSetup
handler's body, has already been set by the onFirstDone
handler. You can always rely on it in sequential execution mode.
The Storage internals are shared between all of its copies. That is why the copies of the Storage object inside the handlers' lambda captures still refer to the same Storage instance. You may place multiple Storage objects inside one Group element, provided that they do not include copies of the same Storage object. Otherwise, an assert is triggered at runtime that includes an error message. However, you can place copies of the same Storage object in different Group elements of the same recipe. In this case, the running task tree will create multiple instances of the StorageStruct
objects (one for each copy) and storage shadowing will take place. Storage shadowing works in a similar way to C++ variable shadowing inside the nested blocks of code:
Storage<QString> storage; const Group root { storage, // Top copy, 1st instance of StorageStruct onGroupSetup([storage] { ... }), // Top copy is active Group { storage, // Nested copy, 2nd instance of StorageStruct, // shadows Top copy onGroupSetup([storage] { ... }), // Nested copy is active }, Group { onGroupSetup([storage] { ... }), // Top copy is active } };
The Storage objects may also be used for passing the initial data to the executed task tree, and for reading the final data out of the task tree before it finishes. To do this, use onStorageSetup() or onStorageDone(), respectively.
Note: If you use an unreachable Storage object inside the handler, because you forgot to place the storage in the recipe, or placed it, but not in any handler's ancestor group, you may expect a crash, preceded by the following message: The referenced storage is not reachable in the running tree. A nullptr will be returned which might lead to a crash in the calling code. It is possible that no storage was added to the tree, or the storage is not reachable from where it is referenced.
Member Function Documentation
Storage::Storage()
Creates a storage for the given StorageStruct
type.
Note: All copies of this
object are considered to be the same Storage instance.
StorageStruct *Storage::activeStorage() const
Returns a pointer to the active StorageStruct
object, created by the running task tree. Use this function only from inside the handler body of any GroupItem element placed in the recipe, otherwise you may expect a crash. Make sure that Storage is placed in any group ancestor of the handler's group item.
Note: The returned pointer is valid as long as the group that created this instance is still running.
See also operator->() and operator*().
[noexcept]
StorageStruct &Storage::operator*() const
Returns a reference to the active StorageStruct
object, created by the running task tree. Use this function only from inside the handler body of any GroupItem element placed in the recipe, otherwise you may expect a crash. Make sure that Storage is placed in any group ancestor of the handler's group item.
Note: The returned reference is valid as long as the group that created this instance is still running.
See also activeStorage() and operator->().
[noexcept]
StorageStruct *Storage::operator->() const
Returns a pointer to the active StorageStruct
object, created by the running task tree. Use this function only from inside the handler body of any GroupItem element placed in the recipe, otherwise you may expect a crash. Make sure that Storage is placed in any group ancestor of the handler's group item.
Note: The returned pointer is valid as long as the group that created this instance is still running.
See also activeStorage() and operator*().
© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.