BsAsyncOp.h 3.1 KB

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