205 lines
5.3 KiB
Text
205 lines
5.3 KiB
Text
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file asyncFuture.I
|
|
* @author rdb
|
|
* @date 2017-11-28
|
|
*/
|
|
|
|
/**
|
|
* Initializes the future in the pending state.
|
|
*/
|
|
INLINE AsyncFuture::
|
|
AsyncFuture() :
|
|
_manager(nullptr),
|
|
_result(nullptr),
|
|
_future_state(FS_pending) {
|
|
}
|
|
|
|
/**
|
|
* Returns true if the future is done or has been cancelled. It is always
|
|
* safe to call this.
|
|
*/
|
|
INLINE bool AsyncFuture::
|
|
done() const {
|
|
return (FutureState)AtomicAdjust::get(_future_state) >= FS_finished;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the future was cancelled. It is always safe to call this.
|
|
*/
|
|
INLINE bool AsyncFuture::
|
|
cancelled() const {
|
|
return (FutureState)AtomicAdjust::get(_future_state) == FS_cancelled;
|
|
}
|
|
|
|
/**
|
|
* Sets the event name that will be triggered when the future finishes. Will
|
|
* not be triggered if the future is cancelled, but it will be triggered for
|
|
* a coroutine task that exits with an exception.
|
|
*/
|
|
INLINE void AsyncFuture::
|
|
set_done_event(const std::string &done_event) {
|
|
nassertv(!done());
|
|
_done_event = done_event;
|
|
}
|
|
|
|
/**
|
|
* Returns the event name that will be triggered when the future finishes.
|
|
* See set_done_event().
|
|
*/
|
|
INLINE const std::string &AsyncFuture::
|
|
get_done_event() const {
|
|
return _done_event;
|
|
}
|
|
|
|
/**
|
|
* Returns this future's result. Can only be called if done() returns true.
|
|
*/
|
|
INLINE TypedObject *AsyncFuture::
|
|
get_result() const {
|
|
// This is thread safe, since _result may no longer be modified after the
|
|
// state is changed to "done".
|
|
nassertr_always(done(), nullptr);
|
|
return _result;
|
|
}
|
|
|
|
/**
|
|
* Returns this future's result as a pair of TypedObject, ReferenceCount
|
|
* pointers. Can only be called if done() returns true.
|
|
*/
|
|
INLINE void AsyncFuture::
|
|
get_result(TypedObject *&ptr, ReferenceCount *&ref_ptr) const {
|
|
// This is thread safe, since _result may no longer be modified after the
|
|
// state is changed to "done".
|
|
nassertd(done()) {
|
|
ptr = nullptr;
|
|
ref_ptr = nullptr;
|
|
}
|
|
ptr = _result;
|
|
ref_ptr = _result_ref.p();
|
|
}
|
|
|
|
/**
|
|
* Sets this future's result. Can only be called if done() returns false.
|
|
*/
|
|
INLINE void AsyncFuture::
|
|
set_result(std::nullptr_t) {
|
|
set_result(nullptr, nullptr);
|
|
}
|
|
|
|
INLINE void AsyncFuture::
|
|
set_result(TypedObject *result) {
|
|
set_result(result, nullptr);
|
|
}
|
|
|
|
INLINE void AsyncFuture::
|
|
set_result(TypedReferenceCount *result) {
|
|
set_result(result, result);
|
|
}
|
|
|
|
INLINE void AsyncFuture::
|
|
set_result(TypedWritableReferenceCount *result) {
|
|
set_result(result, result);
|
|
}
|
|
|
|
INLINE void AsyncFuture::
|
|
set_result(const EventParameter &result) {
|
|
set_result(result.get_ptr(), result.get_ptr());
|
|
}
|
|
|
|
/**
|
|
* Creates a new future that returns `done()` when all of the contained
|
|
* futures are done.
|
|
*
|
|
* Calling `cancel()` on the returned future will result in all contained
|
|
* futures that have not yet finished to be cancelled.
|
|
*/
|
|
INLINE AsyncFuture *AsyncFuture::
|
|
gather(Futures futures) {
|
|
if (futures.empty()) {
|
|
AsyncFuture *fut = new AsyncFuture;
|
|
fut->_future_state = (AtomicAdjust::Integer)FS_finished;
|
|
return fut;
|
|
} else if (futures.size() == 1) {
|
|
return futures[0].p();
|
|
} else {
|
|
return (AsyncFuture *)new AsyncGatheringFuture(std::move(futures));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tries to atomically lock the future, assuming it is pending. Returns false
|
|
* if it is not in the pending state, implying it's either done or about to be
|
|
* cancelled.
|
|
*/
|
|
INLINE bool AsyncFuture::
|
|
try_lock_pending() {
|
|
return set_future_state(FS_locked_pending);
|
|
}
|
|
|
|
/**
|
|
* Should be called after try_lock_pending() returns true.
|
|
*/
|
|
INLINE void AsyncFuture::
|
|
unlock(FutureState new_state) {
|
|
nassertv(new_state != FS_locked_pending);
|
|
FutureState orig_state = (FutureState)AtomicAdjust::set(_future_state, (AtomicAdjust::Integer)new_state);
|
|
nassertv(orig_state == FS_locked_pending);
|
|
}
|
|
|
|
/**
|
|
* Atomically changes the future state from pending to another state. Returns
|
|
* true if successful, false if the future was already done.
|
|
* Note that once a future is in a "done" state (ie. cancelled or finished) it
|
|
* can never change state again.
|
|
*/
|
|
INLINE bool AsyncFuture::
|
|
set_future_state(FutureState state) {
|
|
FutureState orig_state = (FutureState)
|
|
AtomicAdjust::compare_and_exchange(
|
|
_future_state,
|
|
(AtomicAdjust::Integer)FS_pending,
|
|
(AtomicAdjust::Integer)state);
|
|
|
|
while (orig_state == FS_locked_pending) {
|
|
Thread::force_yield();
|
|
orig_state = (FutureState)AtomicAdjust::compare_and_exchange(
|
|
_future_state,
|
|
(AtomicAdjust::Integer)FS_pending,
|
|
(AtomicAdjust::Integer)state);
|
|
}
|
|
|
|
return orig_state == FS_pending;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of futures that were passed to the constructor.
|
|
*/
|
|
INLINE size_t AsyncGatheringFuture::
|
|
get_num_futures() const {
|
|
return _futures.size();
|
|
}
|
|
|
|
/**
|
|
* Returns the nth future that was passed into the constructor.
|
|
*/
|
|
INLINE AsyncFuture *AsyncGatheringFuture::
|
|
get_future(size_t i) const {
|
|
nassertr(i < _futures.size(), nullptr);
|
|
return _futures[i].p();
|
|
}
|
|
|
|
/**
|
|
* Returns the result of the nth future that was passed into the constructor.
|
|
*/
|
|
INLINE TypedObject *AsyncGatheringFuture::
|
|
get_result(size_t i) const {
|
|
nassertr(i < _futures.size(), nullptr);
|
|
return _futures[i]->get_result();
|
|
}
|