BsAsyncOp.h 3.4 KB

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