/** * 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.h * @author rdb * @date 2017-11-28 */ #ifndef ASYNCFUTURE_H #define ASYNCFUTURE_H #include "pandabase.h" #include "typedReferenceCount.h" #include "typedWritableReferenceCount.h" #include "eventParameter.h" #include "atomicAdjust.h" class AsyncTaskManager; class AsyncTask; /** * This class represents a thread-safe handle to a promised future result of * an asynchronous operation, providing methods to query its status and result * as well as register callbacks for this future's completion. * * An AsyncFuture can be awaited from within a coroutine or task. It keeps * track of tasks waiting for this future and automatically reactivates them * upon this future's completion. * * A task itself is also a subclass of AsyncFuture. Other subclasses are * not generally necessary, except to override the function of `cancel()`. * * Until the future is done, it is "owned" by the resolver thread, though it's * still legal for other threads to query its state. When the resolver thread * resolves this future using `set_result()`, or any thread calls `cancel()`, * it instantly enters the "done" state, after which the result becomes a * read-only field that all threads can access. * * When the future returns true for done(), a thread can use cancelled() to * determine whether the future was cancelled or get_result() to access the * result of the operation. Not all operations define a meaningful result * value, so some will always return nullptr. * * In Python, the `cancelled()`, `wait()` and `get_result()` methods are * wrapped up into a single `result()` method which waits for the future to * complete before either returning the result or throwing an exception if the * future was cancelled. * However, it is preferable to use the `await` keyword when running from a * coroutine, which only suspends the current task and not the entire thread. * * This API aims to mirror and be compatible with Python's Future class. * * @since 1.10.0 */ class EXPCL_PANDA_EVENT AsyncFuture : public TypedReferenceCount { PUBLISHED: INLINE AsyncFuture(); virtual ~AsyncFuture(); EXTENSION(static PyObject *__await__(PyObject *self)); EXTENSION(static PyObject *__iter__(PyObject *self)); INLINE bool done() const; INLINE bool cancelled() const; EXTENSION(PyObject *result(PyObject *timeout = Py_None) const); virtual bool cancel(); INLINE void set_done_event(const std::string &done_event); INLINE const std::string &get_done_event() const; MAKE_PROPERTY(done_event, get_done_event, set_done_event); EXTENSION(PyObject *add_done_callback(PyObject *self, PyObject *fn)); EXTENSION(static PyObject *gather(PyObject *args)); virtual void output(std::ostream &out) const; BLOCKING void wait(); BLOCKING void wait(double timeout); INLINE void set_result(std::nullptr_t); INLINE void set_result(TypedObject *result); INLINE void set_result(TypedReferenceCount *result); INLINE void set_result(TypedWritableReferenceCount *result); INLINE void set_result(const EventParameter &result); public: void set_result(TypedObject *ptr, ReferenceCount *ref_ptr); INLINE TypedObject *get_result() const; INLINE void get_result(TypedObject *&ptr, ReferenceCount *&ref_ptr) const; typedef pvector Futures; INLINE static AsyncFuture *gather(Futures futures); virtual bool is_task() const {return false;} void notify_done(bool clean_exit); bool add_waiting_task(AsyncTask *task); private: void wake_task(AsyncTask *task); protected: enum FutureState { // Pending states FS_pending, FS_locked_pending, // Done states FS_finished, FS_cancelled, }; INLINE bool try_lock_pending(); INLINE void unlock(FutureState new_state = FS_pending); INLINE bool set_future_state(FutureState state); AsyncTaskManager *_manager; TypedObject *_result; PT(ReferenceCount) _result_ref; AtomicAdjust::Integer _future_state; std::string _done_event; // Tasks and gathering futures waiting for this one to complete. Futures _waiting; friend class AsyncGatheringFuture; friend class AsyncTaskChain; friend class PythonTask; public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { TypedReferenceCount::init_type(); register_type(_type_handle, "AsyncFuture", TypedReferenceCount::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); } virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; }; INLINE std::ostream &operator << (std::ostream &out, const AsyncFuture &fut) { fut.output(out); return out; }; /** * Specific future that collects the results of several futures. */ class EXPCL_PANDA_EVENT AsyncGatheringFuture final : public AsyncFuture { private: AsyncGatheringFuture(AsyncFuture::Futures futures); public: virtual bool cancel() override; INLINE size_t get_num_futures() const; INLINE AsyncFuture *get_future(size_t i) const; INLINE TypedObject *get_result(size_t i) const; private: const Futures _futures; AtomicAdjust::Integer _num_pending; friend class AsyncFuture; public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { AsyncFuture::init_type(); register_type(_type_handle, "AsyncGatheringFuture", AsyncFuture::get_class_type()); } virtual TypeHandle get_type() const override { return get_class_type(); } virtual TypeHandle force_init_type() override {init_type(); return get_class_type();} private: static TypeHandle _type_handle; }; #include "asyncFuture.I" #endif