BsAsyncOp.h 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. #pragma once
  2. #include "BsPrerequisitesUtil.h"
  3. #include "BsException.h"
  4. #include "BsAny.h"
  5. namespace BansheeEngine
  6. {
  7. /**
  8. * @brief Thread synchronization primitives used by AsyncOps and their
  9. * callers.
  10. */
  11. class BS_UTILITY_EXPORT AsyncOpSyncData
  12. {
  13. public:
  14. BS_MUTEX(mMutex)
  15. BS_THREAD_SYNCHRONISER(mCondition)
  16. };
  17. /**
  18. * @brief Flag used for creating async operations signaling that we want to create an empty
  19. * AsyncOp with no internal memory storage.
  20. */
  21. struct BS_UTILITY_EXPORT AsyncOpEmpty {};
  22. /**
  23. * @brief Object you may use to check on the results of an asynchronous operation.
  24. * Contains uninitialized data until "hasCompleted" returns true.
  25. *
  26. * @note You are allowed (and meant to) to copy this by value.
  27. *
  28. * You'll notice mIsCompleted isn't synchronized. This is because we're okay if
  29. * mIsCompleted reports true a few cycles too late, which is not relevant for practical use.
  30. * And in cases where you need to ensure operation has completed you will usually use some kind
  31. * of synchronization primitive that includes a memory barrier anyway.
  32. */
  33. class BS_UTILITY_EXPORT AsyncOp
  34. {
  35. private:
  36. struct AsyncOpData
  37. {
  38. AsyncOpData()
  39. :mIsCompleted(false)
  40. { }
  41. Any mReturnValue;
  42. volatile std::atomic<bool> mIsCompleted;
  43. };
  44. public:
  45. AsyncOp()
  46. :mData(bs_shared_ptr_new<AsyncOpData>())
  47. { }
  48. AsyncOp(AsyncOpEmpty empty)
  49. { }
  50. AsyncOp(const AsyncOpSyncDataPtr& syncData)
  51. :mData(bs_shared_ptr_new<AsyncOpData>()), mSyncData(syncData)
  52. { }
  53. AsyncOp(AsyncOpEmpty empty, const AsyncOpSyncDataPtr& syncData)
  54. :mSyncData(syncData)
  55. { }
  56. /**
  57. * @brief True if the async operation has completed.
  58. *
  59. * @note Internal method.
  60. */
  61. bool hasCompleted() const;
  62. /**
  63. * @brief Mark the async operation as completed.
  64. *
  65. * @note Internal method.
  66. */
  67. void _completeOperation(Any returnValue);
  68. /**
  69. * @brief Mark the async operation as completed, without setting a return value.
  70. *
  71. * @note Internal method.
  72. */
  73. void _completeOperation();
  74. /**
  75. * @brief Blocks the caller thread until the AsyncOp completes.
  76. *
  77. * @note Internal method.
  78. * Do not call this on the thread that is completing the async op, as it
  79. * will cause a deadlock.
  80. * Make sure the command you are waiting for is actually queued for execution
  81. * because a deadlock will occurr otherwise.
  82. */
  83. void _blockUntilComplete() const;
  84. /**
  85. * @brief Retrieves the value returned by the async operation. Only valid
  86. * if "hasCompleted" returns true.
  87. */
  88. template <typename T>
  89. T getReturnValue() const
  90. {
  91. #if BS_DEBUG_MODE
  92. if(!hasCompleted())
  93. BS_EXCEPT(InternalErrorException, "Trying to get AsyncOp return value but the operation hasn't completed.");
  94. #endif
  95. // Be careful if cast throws an exception. It doesn't support casting of polymorphic types. Provided and returned
  96. // types must be EXACT. (You'll have to cast the data yourself when completing the operation)
  97. return any_cast<T>(mData->mReturnValue);
  98. }
  99. private:
  100. std::shared_ptr<AsyncOpData> mData;
  101. AsyncOpSyncDataPtr mSyncData;
  102. };
  103. }