historical/toontown-classic.git/panda/include/asyncFuture.h
2024-01-16 11:20:27 -06:00

201 lines
5.9 KiB
C++

/**
* 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<PT(AsyncFuture)> 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