1/*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25/** \defgroup threadpool Threadpool related functions
26 * ##Threadpool
27 * \ingroup lwsapi
28 *
29 * This allows you to create one or more pool of threads which can run tasks
30 * associated with a wsi. If the pool is busy, tasks wait on a queue.
31 *
32 * Tasks don't have to be atomic, if they will take more than a few tens of ms
33 * they should return back to the threadpool worker with a return of 0. This
34 * will allow them to abort cleanly.
35 */
36//@{
37
38struct lws_threadpool;
39struct lws_threadpool_task;
40
41enum lws_threadpool_task_status {
42 LWS_TP_STATUS_QUEUED,
43 LWS_TP_STATUS_RUNNING,
44 LWS_TP_STATUS_SYNCING,
45 LWS_TP_STATUS_STOPPING,
46 LWS_TP_STATUS_FINISHED, /* lws_threadpool_task_status() frees task */
47 LWS_TP_STATUS_STOPPED, /* lws_threadpool_task_status() frees task */
48};
49
50enum lws_threadpool_task_return {
51 /** Still work to do, just confirming not being stopped */
52 LWS_TP_RETURN_CHECKING_IN,
53 /** Still work to do, enter cond_wait until service thread syncs. This
54 * is used if you have filled your buffer(s) of data to the service
55 * thread and are blocked until the service thread completes sending at
56 * least one.
57 */
58 LWS_TP_RETURN_SYNC,
59 /** No more work to do... */
60 LWS_TP_RETURN_FINISHED,
61 /** Responding to request to stop */
62 LWS_TP_RETURN_STOPPED,
63
64 /* OR on to indicate this task wishes to outlive its wsi */
65 LWS_TP_RETURN_FLAG_OUTLIVE = 64
66};
67
68struct lws_threadpool_create_args {
69 int threads;
70 int max_queue_depth;
71};
72
73struct lws_threadpool_task_args {
74#if defined(LWS_WITH_SECURE_STREAMS)
75 struct lws_ss_handle *ss; /**< either wsi or ss must be set */
76#endif
77 struct lws *wsi; /**< either wsi or ss must be set */
78
79 void *user; /**< user may set (user-private pointer) */
80 const char *name; /**< user may set to describe task */
81 char async_task; /**< set to allow the task to shrug off the loss
82 of the associated wsi and continue to
83 completion */
84 enum lws_threadpool_task_return (*task)(void *user,
85 enum lws_threadpool_task_status s);
86 /**< user must set to actual task function */
87 void (*cleanup)(struct lws *wsi, void *user);
88 /**< socket lifecycle may end while task is not stoppable, so the task
89 * must be able to detach from any wsi and clean itself up when it does
90 * stop. If NULL, no cleanup necessary, otherwise point to a user-
91 * supplied function that destroys the stuff in \p user.
92 *
93 * wsi may be NULL on entry, indicating the task got detached due to the
94 * wsi closing before.
95 */
96};
97
98/**
99 * lws_threadpool_create() - create a pool of worker threads
100 *
101 * \param context: the lws_context the threadpool will exist inside
102 * \param args: argument struct prepared by caller
103 * \param format: printf-type format for the task name
104 * \param ...: printf type args for the task name format
105 *
106 * Creates a pool of worker threads with \p threads and a queue of up to
107 * \p max_queue_depth waiting tasks if all the threads are busy.
108 *
109 * Returns NULL if OOM, or a struct lws_threadpool pointer that must be
110 * destroyed by lws_threadpool_destroy().
111 */
112LWS_VISIBLE LWS_EXTERN struct lws_threadpool *
113lws_threadpool_create(struct lws_context *context,
114 const struct lws_threadpool_create_args *args,
115 const char *format, ...) LWS_FORMAT(3);
116
117/**
118 * lws_threadpool_finish() - Stop all pending and running tasks
119 *
120 * \param tp: the threadpool object
121 *
122 * Marks the threadpool as under destruction. Removes everything from the
123 * pending queue and completes those tasks as LWS_TP_STATUS_STOPPED.
124 *
125 * Running tasks will also get LWS_TP_STATUS_STOPPED as soon as they
126 * "resurface".
127 *
128 * This doesn't reap tasks or free the threadpool, the reaping is done by the
129 * lws_threadpool_task_status() on the done task.
130 */
131LWS_VISIBLE LWS_EXTERN void
132lws_threadpool_finish(struct lws_threadpool *tp);
133
134/**
135 * lws_threadpool_destroy() - Destroy a threadpool
136 *
137 * \param tp: the threadpool object
138 *
139 * Waits for all worker threads to stop, ends the threads and frees the tp.
140 */
141LWS_VISIBLE LWS_EXTERN void
142lws_threadpool_destroy(struct lws_threadpool *tp);
143
144/**
145 * lws_threadpool_enqueue() - Queue the task and run it on a worker thread when possible
146 *
147 * \param tp: the threadpool to queue / run on
148 * \param args: information about what to run
149 * \param format: printf-type format for the task name
150 * \param ...: printf type args for the task name format
151 *
152 * This asks for a task to run ASAP on a worker thread in threadpool \p tp.
153 *
154 * The args defines the wsi, a user-private pointer, a timeout in secs and
155 * a pointer to the task function.
156 *
157 * Returns NULL or an opaque pointer to the queued (or running, or completed)
158 * task.
159 *
160 * Once a task is created and enqueued, it can only be destroyed by calling
161 * lws_threadpool_task_status() on it after it has reached the state
162 * LWS_TP_STATUS_FINISHED or LWS_TP_STATUS_STOPPED.
163 */
164LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task *
165lws_threadpool_enqueue(struct lws_threadpool *tp,
166 const struct lws_threadpool_task_args *args,
167 const char *format, ...) LWS_FORMAT(3);
168
169/**
170 * lws_threadpool_dequeue() - Dequeue or try to stop a running task
171 *
172 * \param wsi: the wsi whose current task we want to eliminate
173 *
174 * Returns 0 is the task was dequeued or already compeleted, or 1 if the task
175 * has been asked to stop asynchronously.
176 *
177 * This doesn't free the task. It only shortcuts it to state
178 * LWS_TP_STATUS_STOPPED. lws_threadpool_task_status() must be performed on
179 * the task separately once it is in LWS_TP_STATUS_STOPPED to free the task.
180 *
181 * DEPRECATED: You should use lws_threadpool_dequeue_task() with
182 * lws_threadpool_get_task_wsi() / _ss() if you know there can only be one task
183 * per connection, or call it via lws_threadpool_foreach_task_wsi() / _ss() to
184 * get the tasks bound to the connection.
185 */
186LWS_VISIBLE LWS_EXTERN int
187lws_threadpool_dequeue(struct lws *wsi) LWS_WARN_DEPRECATED;
188
189LWS_VISIBLE LWS_EXTERN int
190lws_threadpool_dequeue_task(struct lws_threadpool_task *task);
191
192
193/**
194 * lws_threadpool_task_status() - reap completed tasks
195 *
196 * \param wsi: the wsi to query the current task of
197 * \param task: receives a pointer to the opaque task
198 * \param user: receives a void * pointer to the task user data
199 *
200 * This is the equivalent of posix waitpid()... it returns the status of the
201 * task, and if the task is in state LWS_TP_STATUS_FINISHED or
202 * LWS_TP_STATUS_STOPPED, frees \p task. If in another state, the task
203 * continues to exist.
204 *
205 * This is designed to be called from the service thread.
206 *
207 * Its use is to make sure the service thread has seen the state of the task
208 * before deleting it.
209 *
210 * DEPRECATED... use lws_threadpool_task_status() instead and get the task
211 * pointer from lws_threadpool_get_task_wsi() / _ss() if you know there can only
212 * be one, else call it via lws_threadpool_foreach_task_wsi() / _ss()
213 */
214LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status
215lws_threadpool_task_status_wsi(struct lws *wsi,
216 struct lws_threadpool_task **task, void **user)
217 LWS_WARN_DEPRECATED;
218
219LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status
220lws_threadpool_task_status(struct lws_threadpool_task *task, void **user);
221
222LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status
223lws_threadpool_task_status_noreap(struct lws_threadpool_task *task);
224
225/**
226 * lws_threadpool_task_sync() - Indicate to a stalled task it may continue
227 *
228 * \param task: the task to unblock
229 * \param stop: 0 = run after unblock, 1 = when he unblocks, stop him
230 *
231 * Inform the task that the service thread has finished with the shared data
232 * and that the task, if blocked in LWS_TP_RETURN_SYNC, may continue.
233 *
234 * If the lws service context determined that the task must be aborted, it
235 * should still call this but with stop = 1, causing the task to finish.
236 */
237LWS_VISIBLE LWS_EXTERN void
238lws_threadpool_task_sync(struct lws_threadpool_task *task, int stop);
239
240/**
241 * lws_threadpool_dump() - dump the state of a threadpool to the log
242 *
243 * \param tp: The threadpool to dump
244 *
245 * This locks the threadpool and then dumps the pending queue, the worker
246 * threads and the done queue, together with time information for how long
247 * the tasks have been in their current state, how long they have occupied a
248 * thread, etc.
249 *
250 * This only does anything on lws builds with CMAKE_BUILD_TYPE=DEBUG, otherwise
251 * while it still exists, it's a NOP.
252 */
253
254LWS_VISIBLE LWS_EXTERN void
255lws_threadpool_dump(struct lws_threadpool *tp);
256
257
258
259LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task *
260lws_threadpool_get_task_wsi(struct lws *wsi);
261
262#if defined(LWS_WITH_SECURE_STREAMS)
263LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task *
264lws_threadpool_get_task_ss(struct lws_ss_handle *ss);
265#endif
266
267
268LWS_VISIBLE LWS_EXTERN int
269lws_threadpool_foreach_task_wsi(struct lws *wsi, void *user,
270 int (*cb)(struct lws_threadpool_task *task,
271 void *user));
272
273#if defined(LWS_WITH_SECURE_STREAMS)
274LWS_VISIBLE LWS_EXTERN int
275lws_threadpool_foreach_task_ss(struct lws_ss_handle *ss, void *user,
276 int (*cb)(struct lws_threadpool_task *task, void *user));
277#endif
278
279
280//@}
281