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 | |
38 | struct lws_threadpool; |
39 | struct lws_threadpool_task; |
40 | |
41 | enum 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 | |
50 | enum 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 | |
68 | struct lws_threadpool_create_args { |
69 | int threads; |
70 | int max_queue_depth; |
71 | }; |
72 | |
73 | struct 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 | */ |
112 | LWS_VISIBLE LWS_EXTERN struct lws_threadpool * |
113 | lws_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 | */ |
131 | LWS_VISIBLE LWS_EXTERN void |
132 | lws_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 | */ |
141 | LWS_VISIBLE LWS_EXTERN void |
142 | lws_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 | */ |
164 | LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task * |
165 | lws_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 | */ |
186 | LWS_VISIBLE LWS_EXTERN int |
187 | lws_threadpool_dequeue(struct lws *wsi) LWS_WARN_DEPRECATED; |
188 | |
189 | LWS_VISIBLE LWS_EXTERN int |
190 | lws_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 | */ |
214 | LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status |
215 | lws_threadpool_task_status_wsi(struct lws *wsi, |
216 | struct lws_threadpool_task **task, void **user) |
217 | LWS_WARN_DEPRECATED; |
218 | |
219 | LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status |
220 | lws_threadpool_task_status(struct lws_threadpool_task *task, void **user); |
221 | |
222 | LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status |
223 | lws_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 | */ |
237 | LWS_VISIBLE LWS_EXTERN void |
238 | lws_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 | |
254 | LWS_VISIBLE LWS_EXTERN void |
255 | lws_threadpool_dump(struct lws_threadpool *tp); |
256 | |
257 | |
258 | |
259 | LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task * |
260 | lws_threadpool_get_task_wsi(struct lws *wsi); |
261 | |
262 | #if defined(LWS_WITH_SECURE_STREAMS) |
263 | LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task * |
264 | lws_threadpool_get_task_ss(struct lws_ss_handle *ss); |
265 | #endif |
266 | |
267 | |
268 | LWS_VISIBLE LWS_EXTERN int |
269 | lws_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) |
274 | LWS_VISIBLE LWS_EXTERN int |
275 | lws_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 | |