I need a function that retrieves a task from a thread pool and then executes it.
pub fn helpDoWork (pool: *Pool, id: ?usize) void {
if (id) |unwrap_id| std.debug.assert(pool.ids.getIndex(std.Thread.getCurrentId()) == unwrap_id);
const run_node = blk: {
pool.mutex.lock();
defer pool.mutex.unlock();
break :blk pool.run_queue.popFirst() orelse return;
};
const runnable: *Pool.Runnable = @fieldParentPtr("node", run_node);
runnable.runFn(runnable, id);
}
This feature is helpful for setting a soft cap on tasks in a thread pool. E.g.:
while (condition) {
if (task_in_queue_count.fetchAdd(1, .acquire) > task_queue_threshold) helpDoWork(pool, 0);
pool.spawnWgId(&wait_group, task, .{ &task_in_queue_count, other_args });
}
In this use case, a separately maintained value is used to mark the number of tasks that have not been executed in the thread pool.
pub fn task(thrd_id: usize, task_in_queue_count: *align(std.atomic.cache_line) std.atomic.Value(usize), other_args: anytype) void {
_ = task_in_queue_count.fetchSub(1, .release);
...
}
When the number of tasks waiting to be executed in the thread pool reaches a threshold, the spawn thread (usually thread 0) can help resolve tasks before spawning new ones.
The only problem is that Pool.Runnable is not publicly declared. I’m hoping to use reflection to get this type, but I haven’t been able to find a way to do so.
Ultimately, in order to achieve my helpDoWork, I had to copy the entire std.Thread.Pool and then declare Runnable as pub.
Besides copying the code, is there a better way to obtain a Runnable to extend the functionality of Thread.Pool?