#pragma once #include #include #include "Common/Log.h" #include "Common/Thread/Channel.h" #include "Common/Thread/ThreadManager.h" template class PromiseTask : public Task { public: PromiseTask(std::function fun, Mailbox *tx, TaskType t, TaskPriority p) : fun_(fun), tx_(tx), type_(t), priority_(p) { tx_->AddRef(); } ~PromiseTask() { tx_->Release(); } TaskType Type() const override { return type_; } TaskPriority Priority() const override { return priority_; } void Run() override { T value = fun_(); tx_->Send(value); } std::function fun_; Mailbox *tx_; const TaskType type_; const TaskPriority priority_; }; // Represents pending or actual data. // Has ownership over the data. Single use. // TODO: Split Mailbox (rx_ and tx_) up into separate proxy objects. // NOTE: Poll/BlockUntilReady should only be used from one thread. // TODO: Make movable? template class Promise { public: // Never fails. static Promise *Spawn(ThreadManager *threadman, std::function fun, TaskType taskType, TaskPriority taskPriority = TaskPriority::NORMAL) { Mailbox *mailbox = new Mailbox(); Promise *promise = new Promise(); promise->rx_ = mailbox; PromiseTask *task = new PromiseTask(fun, mailbox, taskType, taskPriority); threadman->EnqueueTask(task); return promise; } static Promise *AlreadyDone(T data) { Promise *promise = new Promise(); promise->data_ = data; promise->ready_ = true; return promise; } static Promise *CreateEmpty() { Mailbox *mailbox = new Mailbox(); Promise *promise = new Promise(); promise->rx_ = mailbox; return promise; } // Allow an empty promise to spawn, too, in case we want to delay it. void SpawnEmpty(ThreadManager *threadman, std::function fun, TaskType taskType, TaskPriority taskPriority = TaskPriority::NORMAL) { PromiseTask *task = new PromiseTask(fun, rx_, taskType, taskPriority); threadman->EnqueueTask(task); } ~Promise() { std::lock_guard guard(readyMutex_); // A promise should have been fulfilled before it's destroyed. _assert_(ready_); _assert_(!rx_); sentinel_ = 0xeeeeeeee; } // Returns T if the data is ready, nullptr if it's not. // Obviously, can only be used if T is nullable, otherwise it won't compile. T Poll() { _assert_(sentinel_ == 0xffc0ffee); std::lock_guard guard(readyMutex_); if (ready_) { return data_; } else { if (rx_->Poll(&data_)) { rx_->Release(); rx_ = nullptr; ready_ = true; return data_; } else { return nullptr; } } } T BlockUntilReady() { _assert_(sentinel_ == 0xffc0ffee); std::lock_guard guard(readyMutex_); if (ready_) { return data_; } else { data_ = rx_->Wait(); rx_->Release(); rx_ = nullptr; ready_ = true; return data_; } } // For outside injection of data, when not using Spawn. void Post(T data) { rx_->Send(data); } private: Promise() {} // Promise can only be constructed in Spawn (or AlreadyDone). T data_{}; bool ready_ = false; std::mutex readyMutex_; Mailbox *rx_ = nullptr; uint32_t sentinel_ = 0xffc0ffee; };