AsyncSaveRunner.h 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #pragma once
  2. /*
  3. * Copyright (c) Contributors to the Open 3D Engine Project.
  4. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  5. *
  6. * SPDX-License-Identifier: Apache-2.0 OR MIT
  7. *
  8. */
  9. #include <AzCore/std/string/string.h>
  10. #include <AzCore/std/functional.h>
  11. #include <AzCore/std/smart_ptr/shared_ptr.h>
  12. #include <AzCore/std/smart_ptr/make_shared.h>
  13. #include <AzCore/std/containers/vector.h>
  14. #include <EditorCommonAPI.h>
  15. /*
  16. ------------------
  17. AsyncSaveRunner:
  18. ------------------
  19. == Overview ==
  20. This class is meant to be a container for 1-n save operations that need to work with async source control commands. The asynchronous aspect of the SourceControlBus becomes
  21. much more difficult when you have many operations because there is a management problem in knowing how soon until all commands have completed. This class will provide you
  22. with an easy to use interface for specifying the save operations, and providing a single callback to be called once all those operations have been completed.
  23. == Note ==
  24. This class accepts lambdas and operates asynchronously. While the callbacks will be called on the main thread, **YOU MUST GUARANTEE LIFETIME YOURSELF**
  25. == Usage ==
  26. To use this class, you need to guarantee the lifetime of the save runner. The best way to do that is to store it as a member variable in the class that runs the save. Storing
  27. it in a pointer type (or smart-pointer type) will help you control it's lifetime and memory.
  28. Once you have a guaranteed lifetime AsyncSaveRunner, you'll build SaveOperationController instances that will manage all of your individual save operations, and will
  29. run the source control pieces for you. This allows you to focus on specifying the pieces you care about.
  30. This is easiest to see why you would want that, would be a 'Save All' scenario.
  31. Lets imagine a save function that saves an item, which consists of saving a "header" file and an "entry" file:
  32. void SaveItem(int index)
  33. {
  34. auto item = m_items[index];
  35. auto controller = m_saveRunner->GenerateController();
  36. controller->AddSaveOperation(m_headerSaver.getPath(), [item](const AZStd::string& fullPath, const AZStd::shared_ptr<ActionOutput>& actionOutput)->bool
  37. {
  38. return item->headerSaver.save();
  39. }
  40. );
  41. controller->AddSaveOperation(m_entry.getPath(), [item](const AZStd::string& fullPath, const AZStd::shared_ptr<ActionOutput>& actionOutput)->bool
  42. {
  43. return item->entry.save();
  44. }
  45. );
  46. }
  47. You can see that the AsyncSaveRunner was used to make a save operation controller, called 'controller' and that controller was filled out with save operations.
  48. If it was desired, you could even add a callback per controller to know when each controller is finished (in case you have to run a custom notification or something
  49. on the item).
  50. You could imagine SaveItem being called 1-n times, adding more and more SaveOperationController instances to the AsyncSaveRunner. Once the runner is all filled out
  51. you call run on it and pass it a callback. This callback will only be called once. This leaves our example to look like this:
  52. void SaveAll(AZStd::shared_ptr<AZ::ActionOutput> output, AZ::SaveCompleteCallback onComplete)
  53. {
  54. m_saveRunner = AZStd::make_shared<AZ::AsyncSaveRunner>();
  55. for(int index = 0; index < m_numItems; ++index)
  56. {
  57. Saveitem(index);
  58. }
  59. m_saveRunner->Run(output,
  60. [this](bool success)
  61. {
  62. m_saveRunner = nullptr;
  63. if(onComplete)
  64. {
  65. onComplete(success);
  66. }
  67. }
  68. );
  69. }
  70. */
  71. namespace AZ
  72. {
  73. class ActionOutput;
  74. using SaveCompleteCallback = AZStd::function<void(bool success)>;
  75. using SynchronousSaveOperation = AZStd::function<bool(const AZStd::string& fullPath, const AZStd::shared_ptr<ActionOutput>& actionOutput)>;
  76. class AsyncSaveRunner;
  77. // Stores a cache of synchronous save operations, and runs them on completion of asynchronous source control operations.
  78. class EDITOR_COMMON_API SaveOperationController
  79. {
  80. public:
  81. explicit SaveOperationController(AsyncSaveRunner& owner);
  82. void AddSaveOperation(const AZStd::string& fullPath, SynchronousSaveOperation saveOperation);
  83. void AddDeleteOperation(const AZStd::string& fullPath, SynchronousSaveOperation saveOperation);
  84. void SetOnCompleteCallback(SaveCompleteCallback onThisRunnerComplete);
  85. void RunAll(const AZStd::shared_ptr<ActionOutput>& actionOutput);
  86. // Caches all synchronous save operations and associated data. Controlled by a SaveOperationController.
  87. class SaveOperationCache
  88. {
  89. public:
  90. SaveOperationCache(const AZStd::string& fullPath, SynchronousSaveOperation saveOperation, SaveOperationController& owner, bool isDelete = false);
  91. void Run(const AZStd::shared_ptr<ActionOutput>& actionOutput);
  92. void RunDelete(const AZStd::shared_ptr<ActionOutput>& actionOutput);
  93. friend class SaveOperationController;
  94. private:
  95. AZStd::string m_fullSavePath;
  96. SynchronousSaveOperation m_saveOperation;
  97. SaveOperationController& m_owner;
  98. bool m_isDelete;
  99. };
  100. void HandleOperationComplete(SaveOperationCache* saveOperation, bool success);
  101. friend class SaveRunner;
  102. private:
  103. AsyncSaveRunner& m_owner;
  104. AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
  105. AZStd::vector<AZStd::shared_ptr<SaveOperationCache>> m_allSaveOperations;
  106. SaveCompleteCallback m_onSaveComplete;
  107. AZStd::atomic<size_t> m_completedCount;
  108. AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
  109. bool m_currentSaveResult = true;
  110. };
  111. // Builds, stores and executes SaveOperationController instances
  112. class EDITOR_COMMON_API AsyncSaveRunner
  113. {
  114. public:
  115. enum class ControllerOrder
  116. {
  117. // Random will run controllers at once and completion will happen randomly.
  118. Random,
  119. // Controllers are executed in order, waiting for one controller before starting
  120. // the next one. Controllers internally will still have their executions
  121. // complete in random order.
  122. Sequential
  123. };
  124. AZStd::shared_ptr<SaveOperationController> GenerateController();
  125. void Run(const AZStd::shared_ptr<ActionOutput>& actionOutput, SaveCompleteCallback onSaveAllComplete, ControllerOrder order);
  126. private:
  127. friend class SaveOperationController;
  128. void HandleRunnerFinished(SaveOperationController* runner, bool success);
  129. AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
  130. AZStd::vector<AZStd::shared_ptr<SaveOperationController>> m_allSaveControllers;
  131. SaveCompleteCallback m_onSaveAllComplete;
  132. AZStd::shared_ptr<ActionOutput> m_actionOutput;
  133. // If controller order is random this keeps track of the number of completed tasks, if the order is sequential it
  134. // keeps track of the currently executing controller.
  135. AZStd::atomic<size_t> m_counter;
  136. AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
  137. ControllerOrder m_order;
  138. bool m_allWereSuccessfull = true;
  139. };
  140. }