| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- #pragma once
- #include "BsPrerequisitesUtil.h"
- #include "BsException.h"
- #include "BsAny.h"
- namespace BansheeEngine
- {
- /**
- * @brief Thread synchronization primitives used by AsyncOps and their
- * callers.
- */
- class BS_UTILITY_EXPORT AsyncOpSyncData
- {
- public:
- BS_MUTEX(mMutex)
- BS_THREAD_SYNCHRONISER(mCondition)
- };
- /**
- * @brief Flag used for creating async operations signaling that we want to create an empty
- * AsyncOp with no internal memory storage.
- */
- struct BS_UTILITY_EXPORT AsyncOpEmpty {};
- /**
- * @brief Object you may use to check on the results of an asynchronous operation.
- * Contains uninitialized data until "hasCompleted" returns true.
- *
- * @note You are allowed (and meant to) to copy this by value.
- *
- * You'll notice mIsCompleted isn't synchronized. This is because we're okay if
- * mIsCompleted reports true a few cycles too late, which is not relevant for practical use.
- * And in cases where you need to ensure operation has completed you will usually use some kind
- * of synchronization primitive that includes a memory barrier anyway.
- */
- class BS_UTILITY_EXPORT AsyncOp
- {
- private:
- struct AsyncOpData
- {
- AsyncOpData()
- :mIsCompleted(false)
- { }
- Any mReturnValue;
- volatile std::atomic<bool> mIsCompleted;
- };
- public:
- AsyncOp()
- :mData(bs_shared_ptr_new<AsyncOpData>())
- { }
- AsyncOp(AsyncOpEmpty empty)
- { }
- AsyncOp(const AsyncOpSyncDataPtr& syncData)
- :mData(bs_shared_ptr_new<AsyncOpData>()), mSyncData(syncData)
- { }
- AsyncOp(AsyncOpEmpty empty, const AsyncOpSyncDataPtr& syncData)
- :mSyncData(syncData)
- { }
- /**
- * @brief True if the async operation has completed.
- *
- * @note Internal method.
- */
- bool hasCompleted() const;
- /**
- * @brief Mark the async operation as completed.
- *
- * @note Internal method.
- */
- void _completeOperation(Any returnValue);
- /**
- * @brief Mark the async operation as completed, without setting a return value.
- *
- * @note Internal method.
- */
- void _completeOperation();
- /**
- * @brief Blocks the caller thread until the AsyncOp completes.
- *
- * @note Internal method.
- * Do not call this on the thread that is completing the async op, as it
- * will cause a deadlock.
- * Make sure the command you are waiting for is actually queued for execution
- * because a deadlock will occurr otherwise.
- */
- void _blockUntilComplete() const;
- /**
- * @brief Retrieves the value returned by the async operation. Only valid
- * if "hasCompleted" returns true.
- */
- template <typename T>
- T getReturnValue() const
- {
- #if BS_DEBUG_MODE
- if(!hasCompleted())
- BS_EXCEPT(InternalErrorException, "Trying to get AsyncOp return value but the operation hasn't completed.");
- #endif
- // Be careful if cast throws an exception. It doesn't support casting of polymorphic types. Provided and returned
- // types must be EXACT. (You'll have to cast the data yourself when completing the operation)
- return any_cast<T>(mData->mReturnValue);
- }
- private:
- std::shared_ptr<AsyncOpData> mData;
- AsyncOpSyncDataPtr mSyncData;
- };
- }
|