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