| /* | |
| * Copyright 2026 Rive | |
| */ | |
| #pragma once | |
| #include "rive/async/work_task.hpp" | |
| #include "rive/refcnt.hpp" | |
| #include <atomic> | |
| #include <deque> | |
| #include <cstdint> | |
| #ifdef WITH_RIVE_THREADING | |
| #include <thread> | |
| #include <mutex> | |
| #include <condition_variable> | |
| #include <vector> | |
| #include <unordered_map> | |
| #endif | |
| namespace rive | |
| { | |
| /// A pool that executes WorkTasks. | |
| /// | |
| /// WITH_RIVE_THREADING: spawns worker threads; submit() queues work; | |
| /// pollCompletedWork() delivers callbacks on the main thread. | |
| /// | |
| /// Without threading: submit() just enqueues; pollCompletedWork() dequeues, | |
| /// calls execute() synchronously, then delivers callbacks — all on the main | |
| /// thread. | |
| class WorkPool : public RefCnt<WorkPool> | |
| { | |
| public: | |
| WorkPool(); | |
| ~WorkPool(); | |
| /// Submit a task for execution. Returns a nonzero handle on success. | |
| uint64_t submit(rcp<WorkTask> task); | |
| /// Process up to maxCallbacks completed tasks on the main thread. | |
| /// Returns the number of tasks processed. | |
| uint32_t pollCompletedWork(uint32_t maxCallbacks = 16); | |
| /// Returns true if any tasks are queued, running, or | |
| /// completed-but-unpolled. | |
| bool hasPendingWork() const; | |
| /// Cancel all tasks belonging to the given owner. | |
| void cancelAllForOwner(uint64_t ownerId); | |
| /// Generate a unique owner ID for task tracking. | |
| static uint64_t nextOwnerId(); | |
| private: | |
| std::deque<rcp<WorkTask>> m_workQueue; | |
| #ifdef WITH_RIVE_THREADING | |
| std::deque<rcp<WorkTask>> m_completedQueue; | |
| std::vector<std::thread> m_threads; | |
| std::mutex m_queueMutex; | |
| std::mutex m_completedMutex; | |
| std::condition_variable m_haveWork; | |
| // Maps ownerId → cancel generation. A task is owner-cancelled only if | |
| // its submit generation is less than the owner's cancel generation. | |
| std::unordered_map<uint64_t, uint64_t> m_cancelledOwners; | |
| std::atomic<int> m_inFlightCount{0}; | |
| uint64_t m_cancelGeneration = 0; | |
| bool m_shutdown = false; | |
| void workerLoop(); | |
| #endif | |
| uint64_t m_nextHandle = 1; | |
| static std::atomic<uint64_t> s_nextOwnerId; | |
| }; | |
| /// Returns the process-global WorkPool singleton. Creates it on first call. | |
| rcp<WorkPool>& getGlobalWorkPool(); | |
| /// Returns the global WorkPool if it has been created, or an empty rcp if not. | |
| rcp<WorkPool>& getGlobalWorkPoolIfExists(); | |
| /// Poll the global WorkPool for completed async tasks (image decodes, etc.). | |
| /// Called from Artboard::advance() so promises resolve before script callbacks. | |
| /// Safe to call even if no WorkPool exists (no-op). | |
| void rive_pollAsyncWork(); | |
| } // namespace rive |