Mstdlib-1.24.0
m_threadpool.h
1/* The MIT License (MIT)
2 *
3 * Copyright (c) 2015 Monetra Technologies, LLC.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#ifndef __M_THREADPOOL_H__
25#define __M_THREADPOOL_H__
26
27/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
28
29#include <mstdlib/base/m_defs.h>
30#include <mstdlib/base/m_types.h>
31#include <mstdlib/thread/m_thread.h>
32
33/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
34
35__BEGIN_DECLS
36
37/*! \addtogroup m_threadpool Thread Pool
38 * \ingroup m_thread
39 *
40 * Implementation of a thread pool for having a limited the number of threads available
41 * to workers. Threads in the pool will only be destroyed when the pool is destroyed.
42 * A maximum number of threads will be created by the pool. Workers are assigned to
43 * parents which can be used to logically separate workers by tasks.
44 *
45 * Example:
46 *
47 * \code{.c}
48 * static M_uint32 count = 0;
49 *
50 * static void pool_task(void *arg)
51 * {
52 * (void)arg;
53 * M_atomic_inc_u32(&count);
54 * }
55 *
56 * int main(int argc, char **argv)
57 * {
58 * M_threadpool_t *pool;
59 * M_threadpool_parent_t *parent;
60 * char args[32];
61 *
62 * M_mem_set(args, 0, sizeof(args));
63 *
64 * pool = M_threadpool_create(16, 16, 0, SIZE_MAX);
65 * parent = M_threadpool_parent_create(pool);
66 *
67 * M_threadpool_dispatch(parent, pool_task, (void **)&args, sizeof(args));
68 * M_threadpool_parent_wait(parent);
69 *
70 * M_threadpool_parent_destroy(parent);
71 * M_threadpool_destroy(pool);
72 *
73 * M_printf("count='%u'\n", count);
74 *
75 * return 0;
76 * }
77 * \endcode
78 *
79 * @{
80 */
81
82struct M_threadpool;
83typedef struct M_threadpool M_threadpool_t;
84
85struct M_threadpool_parent;
86typedef struct M_threadpool_parent M_threadpool_parent_t;
87
88/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
89
90/*! Initializes a new threadpool and spawns the minimum number of threads requested.
91 *
92 * \param[in] min_threads Minimum number of threads to spawn, 0 to not pre-spawn
93 * any.
94 * \param[in] max_threads Maximum number of threads to spawn, any number above the
95 * min_threads number will be spawned on demand, and idle
96 * threads will be shutdown after the specified idle time.
97 * Must be greater than 0.
98 * \param[in] idle_time_ms Number of milliseconds a thread can be idle for before
99 * it is destroyed when the total thread count is above
100 * min_threads. If min_threads and max_threads are the same
101 * value, this parameter is ignored. Use M_UINT64_MAX to
102 * never terminate an idle thread, or use 0 to never allow
103 * idle threads.
104 * \param[in] queue_max_size If 0, will calculate a desirable queue size based on the
105 * maximum thread count. Otherwise, must be at least the size
106 * of the thread pool. It often makes sense to have the queue
107 * larger than the threadpool size to prevent the threads from
108 * sleeping. When inserting into the queue, if there are no
109 * available slots the M_threadpool_dispatch() function will
110 * block. If blocking is not desirable, use SIZE_MAX to
111 * allow an unbounded number of queue slots.
112 *
113 * \return initialized threadpool or NULL on failure
114 */
115M_API M_threadpool_t *M_threadpool_create(size_t min_threads, size_t max_threads, M_uint64 idle_time_ms, size_t queue_max_size);
116
117
118/*! Shuts down the thread pool, waits for all threads to exit.
119 *
120 * \param[in] pool initialized threadpool.
121 */
123
124
125/*! Creates a new parent/user/consumer of the threadpool.
126 *
127 * This is the handle used to insert tasks and wait for task completion
128 * specific to the consumer.
129 *
130 * It is safe to share this handle across multiple threads if convenient
131 * as long as it is guaranteed to not be destroyed until all consumers are done
132 * using it. If sharing across multiple threads, it probably would mean you
133 * would not be using M_threadpool_parent_wait() from multiple threads
134 * simultaneously.
135 *
136 * \param[in] pool Initialized thread pool.
137 *
138 * \return initialized parent/user/consumer handle.
139 */
141
142
143/*! Frees the parent handle.
144 *
145 * There must be no oustanding tasks prior to calling this. Call
146 * M_threadpool_parent_wait() first if unsure to wait on all tasks to complete.
147 *
148 * \param[in] parent Initialized parent handle.
149 *
150 * \return M_FALSE if there are tasks remaining, M_TRUE if successfully
151 * cleaned up.
152 */
154
155
156/*! Dispatch a task or set of tasks to the threadpool. Identical to
157 * M_threadpool_dispatch_notify() if passed a NULL finished argument.
158 *
159 * Requires a callback function to do the processing and an argument that is
160 * passed to the function. There is no way to retrieve a return value from the
161 * task, so the argument passed to the task should hold a result parameter if
162 * it is necessary to know the completion status. Multiple tasks may be queued
163 * simultaneously.
164 *
165 * This may take a while to complete if there are no queue slots available.
166 *
167 * \param[in,out] parent Initialized parent handle.
168 * \param[in] task Task callback.
169 * \param[in,out] task_args Argument array to pass to each task (one per task).
170 * \param[in] num_tasks total number of tasks being enqueued.
171 */
172M_API void M_threadpool_dispatch(M_threadpool_parent_t *parent, void (*task)(void *), void **task_args, size_t num_tasks);
173
174
175/*! Dispatch a task or set of tasks to the threadpool and notify on task completion.
176 *
177 * Requires a callback function to do the processing and an argument that is
178 * passed to the function. There is no way to retrieve a return value from the
179 * task, so the argument passed to the task should hold a result parameter if
180 * it is necessary to know the completion status. Multiple tasks may be queued
181 * simultaneously.
182 *
183 * This may take a while to complete if there are no queue slots available.
184 *
185 * \param[in,out] parent Initialized parent handle.
186 * \param[in] task Task callback.
187 * \param[in,out] task_args Argument array to pass to each task (one per task).
188 * \param[in] num_tasks total number of tasks being enqueued.
189 * \param[in] finished Optional. Callback to call for each task completion.
190 * Will pass the callback the same argument passed to the task.
191 * Use NULL if no notification desired.
192 */
193M_API void M_threadpool_dispatch_notify(M_threadpool_parent_t *parent, void (*task)(void *), void **task_args, size_t num_tasks, void (*finished)(void *));
194
195
196/*! Count the number of queue slots available to be enqueued for a threadpool.
197 *
198 * \param[in] pool initialized threadpool.
199 */
201
202
203/*! Wait for a thread to become available for processing tasks.
204 *
205 * This explicitly waits for a THREAD and NOT an available queue slot which
206 * there could be available slots. This is meant as an optimization in some
207 * instances where you want to ensure you enqueue some things together,
208 * especially if you're trying to manage SQL locks for tasks being performed.
209 * Typically though, this function would never be used.
210 *
211 * \param[in] parent Initialized parent handle.
212 */
214
215
216/*! Get the current count of the number of threads in the thread pool.
217 *
218 * \param[in] pool Initialized pool handle.
219 * \return count of threads
220 */
221M_API size_t M_threadpool_num_threads(const M_threadpool_t *pool);
222
223
224/*! Wait for all queued tasks to complete then return.
225 *
226 * This is a blocking function with no return value. It is not recommended to
227 * call this from mulitiple threads simultaneously.
228 *
229 * \param[in] parent the initialized parent/user/consumer handle.
230 */
232
233
234/*! @} */
235
236__END_DECLS
237
238#endif /* __M_THREADPOOL_H__ */
void M_threadpool_dispatch(M_threadpool_parent_t *parent, void(*task)(void *), void **task_args, size_t num_tasks)
M_threadpool_t * M_threadpool_create(size_t min_threads, size_t max_threads, M_uint64 idle_time_ms, size_t queue_max_size)
void M_threadpool_wait_available_thread(M_threadpool_parent_t *parent)
M_bool M_threadpool_parent_destroy(M_threadpool_parent_t *parent)
struct M_threadpool_parent M_threadpool_parent_t
Definition: m_threadpool.h:86
struct M_threadpool M_threadpool_t
Definition: m_threadpool.h:83
M_threadpool_parent_t * M_threadpool_parent_create(M_threadpool_t *pool)
size_t M_threadpool_available_slots(const M_threadpool_t *pool)
size_t M_threadpool_num_threads(const M_threadpool_t *pool)
void M_threadpool_dispatch_notify(M_threadpool_parent_t *parent, void(*task)(void *), void **task_args, size_t num_tasks, void(*finished)(void *))
void M_threadpool_destroy(M_threadpool_t *pool)
void M_threadpool_parent_wait(M_threadpool_parent_t *parent)