2020-08-23 17:49:23 -07:00
|
|
|
#ifndef VOXEL_THREAD_POOL_H
|
|
|
|
#define VOXEL_THREAD_POOL_H
|
|
|
|
|
2020-09-14 11:33:02 -07:00
|
|
|
#include "../storage/voxel_buffer.h"
|
2020-08-23 17:49:23 -07:00
|
|
|
#include "../util/array_slice.h"
|
|
|
|
#include "../util/fixed_array.h"
|
|
|
|
#include <core/os/mutex.h>
|
|
|
|
|
|
|
|
#include <queue>
|
|
|
|
|
|
|
|
class Mutex;
|
|
|
|
class Thread;
|
|
|
|
class Semaphore;
|
|
|
|
|
|
|
|
struct VoxelTaskContext {
|
|
|
|
uint8_t thread_index;
|
|
|
|
};
|
|
|
|
|
|
|
|
class IVoxelTask {
|
|
|
|
public:
|
|
|
|
virtual ~IVoxelTask() {}
|
|
|
|
|
|
|
|
virtual void run(VoxelTaskContext ctx) = 0;
|
|
|
|
|
|
|
|
// Lower values means higher priority
|
|
|
|
virtual int get_priority() { return 0; }
|
|
|
|
|
|
|
|
virtual bool is_cancelled() { return false; }
|
|
|
|
};
|
|
|
|
|
|
|
|
// Generic thread pool that performs batches of tasks based on priority
|
|
|
|
class VoxelThreadPool {
|
|
|
|
public:
|
|
|
|
static const uint32_t MAX_THREADS = 8;
|
|
|
|
|
|
|
|
enum State {
|
|
|
|
STATE_RUNNING = 0,
|
|
|
|
STATE_PICKING,
|
2020-08-26 13:17:57 -07:00
|
|
|
STATE_WAITING,
|
2020-08-23 17:49:23 -07:00
|
|
|
STATE_STOPPED
|
|
|
|
};
|
|
|
|
|
|
|
|
VoxelThreadPool();
|
|
|
|
~VoxelThreadPool();
|
|
|
|
|
2021-02-08 16:52:26 -08:00
|
|
|
// Set name prefix to recognize threads of this pool in debug tools.
|
|
|
|
// Must be called before configuring thread count.
|
|
|
|
void set_name(String name);
|
|
|
|
|
2020-08-23 17:49:23 -07:00
|
|
|
// TODO Add ability to change it while running without skipping tasks
|
|
|
|
// Can't be changed after tasks have been queued
|
|
|
|
void set_thread_count(uint32_t count);
|
|
|
|
uint32_t get_thread_count() const { return _thread_count; }
|
|
|
|
|
|
|
|
// TODO Add ability to change it while running
|
|
|
|
// Can't be changed after tasks have been queued
|
|
|
|
void set_batch_count(uint32_t count);
|
|
|
|
|
|
|
|
// TODO Add ability to change it while running
|
|
|
|
// Can't be changed after tasks have been queued
|
|
|
|
void set_priority_update_period(uint32_t milliseconds);
|
|
|
|
|
|
|
|
// Schedules a task.
|
|
|
|
// Ownership is NOT passed to the pool, so make sure you get them back when completed if you want to delete them.
|
|
|
|
void enqueue(IVoxelTask *task);
|
2020-08-25 15:00:38 -07:00
|
|
|
void enqueue(ArraySlice<IVoxelTask *> tasks);
|
2020-08-23 17:49:23 -07:00
|
|
|
|
2021-01-16 05:41:46 -08:00
|
|
|
// TODO Lambda might not be the best API. memcpying to a vector would ensure we lock for a shorter time.
|
2020-08-23 17:49:23 -07:00
|
|
|
template <typename F>
|
|
|
|
void dequeue_completed_tasks(F f) {
|
|
|
|
MutexLock lock(_completed_tasks_mutex);
|
|
|
|
for (size_t i = 0; i < _completed_tasks.size(); ++i) {
|
|
|
|
IVoxelTask *task = _completed_tasks[i];
|
|
|
|
f(task);
|
|
|
|
}
|
|
|
|
_completed_tasks.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Blocks and wait for all tasks to finish (assuming no more are getting added!)
|
|
|
|
void wait_for_all_tasks();
|
|
|
|
|
|
|
|
State get_thread_debug_state(uint32_t i) const;
|
2020-08-26 13:17:57 -07:00
|
|
|
unsigned int get_debug_remaining_tasks() const;
|
2020-08-23 17:49:23 -07:00
|
|
|
|
|
|
|
private:
|
|
|
|
struct TaskItem {
|
|
|
|
IVoxelTask *task = nullptr;
|
|
|
|
int cached_priority = 99999;
|
|
|
|
uint32_t last_priority_update_time = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ThreadData {
|
|
|
|
Thread *thread = nullptr;
|
|
|
|
VoxelThreadPool *pool = nullptr;
|
|
|
|
uint32_t index = 0;
|
|
|
|
bool stop = false;
|
|
|
|
bool waiting = false;
|
|
|
|
State debug_state = STATE_STOPPED;
|
2021-02-08 16:52:26 -08:00
|
|
|
String name;
|
2020-08-23 17:49:23 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static void thread_func_static(void *p_data);
|
|
|
|
void thread_func(ThreadData &data);
|
|
|
|
|
|
|
|
void create_thread(ThreadData &d, uint32_t i);
|
2020-08-28 10:31:21 -07:00
|
|
|
void destroy_all_threads();
|
2020-08-23 17:49:23 -07:00
|
|
|
|
|
|
|
FixedArray<ThreadData, MAX_THREADS> _threads;
|
|
|
|
uint32_t _thread_count = 0;
|
|
|
|
|
2021-01-16 05:41:46 -08:00
|
|
|
// TODO Optimize this with a less naive design? Maybe moodycamel
|
2020-08-23 17:49:23 -07:00
|
|
|
std::vector<TaskItem> _tasks;
|
2021-01-16 05:41:46 -08:00
|
|
|
Mutex *_tasks_mutex = nullptr;
|
2020-08-23 17:49:23 -07:00
|
|
|
Semaphore *_tasks_semaphore = nullptr;
|
|
|
|
|
|
|
|
std::vector<IVoxelTask *> _completed_tasks;
|
|
|
|
Mutex *_completed_tasks_mutex = nullptr;
|
|
|
|
|
|
|
|
uint32_t _batch_count = 1;
|
|
|
|
uint32_t _priority_update_period = 32;
|
2020-08-26 13:17:57 -07:00
|
|
|
|
2021-02-08 16:52:26 -08:00
|
|
|
String _name;
|
|
|
|
|
2020-08-26 13:17:57 -07:00
|
|
|
unsigned int _debug_received_tasks = 0;
|
|
|
|
unsigned int _debug_completed_tasks = 0;
|
2020-08-23 17:49:23 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif // VOXEL_THREAD_TASK_MANAGER_H
|