/* * This software is licensed under the terms of the MIT License. * See COPYING for further information. * --- * Copyright (c) 2011-2024, Lukas Weber . * Copyright (c) 2012-2024, Andrei Alexeyev . */ #pragma once #include "taisei.h" #include "thread.h" typedef struct TaskManager TaskManager; typedef struct Task Task; typedef enum TaskStatus { TASK_INVALID, /** Indicates an error */ TASK_PENDING, /** Task is still in queue and can be cancelled */ TASK_RUNNING, /** Task is currently executing and can not be cancelled */ TASK_CANCELLED, /** Task has been cancelled and will not run */ TASK_FINISHED, /** Task has finished executing and has been removed from the queue */ } TaskStatus; typedef void *(*task_func_t)(void *userdata); typedef void (*task_free_func_t)(void *userdata); /** * Parameters for `taskmgr_submit`. See its documentation below. */ typedef struct TaskParams { /** * The function implementing the task. Must not be NULL. * The return value may be anything. It can be obtained with `task_wait` or `task_finish`. */ task_func_t callback; /** * Value passed as argument to callback. */ void *userdata; /** * Unless NULL, this function will be called when userdata should be freed. * You should use it instead of releasing resources in the task function. It's guaranteed to * be called even if the task has been cancelled. * * This function may be called from any thread, so be careful what you do with it. */ task_free_func_t userdata_free_callback; /** * Priority of the task. Lower values mean higher priority. Higher priority tasks are added * to the queue ahead of the lower priority ones, and thus will start executing sooner. Note * that this affects only the pending tasks. A task that already began executing cannot be * interrupted, regardless of its priority. */ int prio; /** * If true, this task will be inserted ahead of the others with the same priority, if any. * Otherwise, it'll be put behind them instead. */ bool topmost; } TaskParams; /** * Create a new TaskManager with [numthreads] worker threads, and set their priority to [prio]. * If [numthreads] is 0, a default based on the amount of system's CPU cores will be used. * The actual amount of threads spawned may be lower than requested. * * [name] is used to form names of the worker threads. Keep it short. * * On success, returns a pointer to the created TaskManager. * On failure, returns NULL. */ TaskManager *taskmgr_create(uint numthreads, ThreadPriority prio, const char *name) attr_nodiscard attr_returns_max_aligned attr_nonnull(3); /** * Submit a new task to [mgr] described by [params]. It is generally placed at the end of the * task manager's queue, but that can be influenced with [params.prio] and [params.topmost]. * * See documentation for TaskParams above. * * However, you should not rely on the tasks being actually executed in any specific order, in * particular if the task manager is multi-threaded. * * On success, returns a pointer to a Task structure, which must be eventually passed to one of * `task_detach`, `task_finish`, or `task_abort`. Not doing so is a resource leak. * * On failure, returns NULL. */ Task *taskmgr_submit(TaskManager *mgr, TaskParams params) attr_nonnull(1) attr_nodiscard attr_returns_max_aligned; /** * Returns the number of remaining tasks in [mgr]'s queue. */ uint taskmgr_remaining(TaskManager *mgr) attr_nonnull(1); /** * Wait for all remaining tasks to complete, then destroy [mgr], freeing associated resources. * [mgr] must be treated as an invalid pointer as soon as this function is called. */ void taskmgr_finish(TaskManager *mgr) attr_nonnull(1); /** * Cancel all pending tasks, wait for all running tasks to complete, then destroy [mgr], freeing * associated resources. [mgr] must be treated as an invalid pointer as soon as this function * is called. */ void taskmgr_abort(TaskManager *mgr) attr_nonnull(1); /** * Returns the current status of [task]. See TaskStatus documentation above. * Returns TASK_INVALID on failure. */ TaskStatus task_status(Task *task); /** * Wait for [task] to complete. If the task is not running yet, it will be removed from the queue and * executed on the current thread instead. * * On success, returns true and stores the task's return value in [result] (unless [result] is NULL). * On failure, returns false; [result] is left untouched. */ bool task_wait(Task *task, void **result); /** * Cancel a pending [task]. * This does not stop an already running task. * * Returns true on success, false on failure. */ bool task_cancel(Task *task); /** * Disown the [task]. Resources associated with this task will be eventually freed by its TaskManager, * or immediately after this call if the task is no longer in the queue. Use this when you are done * with the task. * * [task] must be treated as an invalid pointer as soon as this function is called. * * Returns true on success, false on failure. */ bool task_detach(Task *task); /** * Wait for [task] to complete, then disown it. * Functionally equivalent to `task_wait` followed by `task_detach`. */ bool task_finish(Task *task, void **result); /** * Attempt to cancel the [task], then disown it. * Functionally equivalent to `task_cancel` followed by `task_detach`. */ bool task_abort(Task *task); /** * Initialize the global task manager with default parameters. */ void taskmgr_global_init(void); /** * Wait for tasks submitted to the global task manager to finish, then destroy * it, freeing all associated resources. */ void taskmgr_global_shutdown(void); /** * Submit a task to the global task manager. See `taskmgr_submit`. */ Task *taskmgr_global_submit(TaskParams params);