1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2019 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 	struct lws *wsi;	/**< user must set to wsi task is bound to */
75 	void *user;		/**< user may set (user-private pointer) */
76 	const char *name;	/**< user may set to describe task */
77 	char async_task;	/**< set to allow the task to shrug off the loss
78 				     of the associated wsi and continue to
79 				     completion */
80 	enum lws_threadpool_task_return (*task)(void *user,
81 					enum lws_threadpool_task_status s);
82 	/**< user must set to actual task function */
83 	void (*cleanup)(struct lws *wsi, void *user);
84 	/**< socket lifecycle may end while task is not stoppable, so the task
85 	 * must be able to detach from any wsi and clean itself up when it does
86 	 * stop.  If NULL, no cleanup necessary, otherwise point to a user-
87 	 * supplied function that destroys the stuff in \p user.
88 	 *
89 	 * wsi may be NULL on entry, indicating the task got detached due to the
90 	 * wsi closing before.
91 	 */
92 };
93 
94 /**
95  * lws_threadpool_create() - create a pool of worker threads
96  *
97  * \param context: the lws_context the threadpool will exist inside
98  * \param args: argument struct prepared by caller
99  * \param format: printf-type format for the task name
100  * \param ...: printf type args for the task name format
101  *
102  * Creates a pool of worker threads with \p threads and a queue of up to
103  * \p max_queue_depth waiting tasks if all the threads are busy.
104  *
105  * Returns NULL if OOM, or a struct lws_threadpool pointer that must be
106  * destroyed by lws_threadpool_destroy().
107  */
108 LWS_VISIBLE LWS_EXTERN struct lws_threadpool *
109 lws_threadpool_create(struct lws_context *context,
110 		      const struct lws_threadpool_create_args *args,
111 		      const char *format, ...) LWS_FORMAT(3);
112 
113 /**
114  * lws_threadpool_finish() - Stop all pending and running tasks
115  *
116  * \param tp: the threadpool object
117  *
118  * Marks the threadpool as under destruction.  Removes everything from the
119  * pending queue and completes those tasks as LWS_TP_STATUS_STOPPED.
120  *
121  * Running tasks will also get LWS_TP_STATUS_STOPPED as soon as they
122  * "resurface".
123  *
124  * This doesn't reap tasks or free the threadpool, the reaping is done by the
125  * lws_threadpool_task_status() on the done task.
126  */
127 LWS_VISIBLE LWS_EXTERN void
128 lws_threadpool_finish(struct lws_threadpool *tp);
129 
130 /**
131  * lws_threadpool_destroy() - Destroy a threadpool
132  *
133  * \param tp: the threadpool object
134  *
135  * Waits for all worker threads to stop, ends the threads and frees the tp.
136  */
137 LWS_VISIBLE LWS_EXTERN void
138 lws_threadpool_destroy(struct lws_threadpool *tp);
139 
140 /**
141  * lws_threadpool_enqueue() - Queue the task and run it on a worker thread when possible
142  *
143  * \param tp: the threadpool to queue / run on
144  * \param args: information about what to run
145  * \param format: printf-type format for the task name
146  * \param ...: printf type args for the task name format
147  *
148  * This asks for a task to run ASAP on a worker thread in threadpool \p tp.
149  *
150  * The args defines the wsi, a user-private pointer, a timeout in secs and
151  * a pointer to the task function.
152  *
153  * Returns NULL or an opaque pointer to the queued (or running, or completed)
154  * task.
155  *
156  * Once a task is created and enqueued, it can only be destroyed by calling
157  * lws_threadpool_task_status() on it after it has reached the state
158  * LWS_TP_STATUS_FINISHED or LWS_TP_STATUS_STOPPED.
159  */
160 LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task *
161 lws_threadpool_enqueue(struct lws_threadpool *tp,
162 		       const struct lws_threadpool_task_args *args,
163 		       const char *format, ...) LWS_FORMAT(3);
164 
165 /**
166  * lws_threadpool_dequeue() - Dequeue or try to stop a running task
167  *
168  * \param wsi: the wsi whose current task we want to eliminate
169  *
170  * Returns 0 is the task was dequeued or already compeleted, or 1 if the task
171  * has been asked to stop asynchronously.
172  *
173  * This doesn't free the task.  It only shortcuts it to state
174  * LWS_TP_STATUS_STOPPED.  lws_threadpool_task_status() must be performed on
175  * the task separately once it is in LWS_TP_STATUS_STOPPED to free the task.
176  */
177 LWS_VISIBLE LWS_EXTERN int
178 lws_threadpool_dequeue(struct lws *wsi);
179 
180 /**
181  * lws_threadpool_task_status() - Dequeue or try to stop a running task
182  *
183  * \param wsi: the wsi to query the current task of
184  * \param task: receives a pointer to the opaque task
185  * \param user: receives a void * pointer to the task user data
186  *
187  * This is the equivalent of posix waitpid()... it returns the status of the
188  * task, and if the task is in state LWS_TP_STATUS_FINISHED or
189  * LWS_TP_STATUS_STOPPED, frees \p task.  If in another state, the task
190  * continues to exist.
191  *
192  * This is designed to be called from the service thread.
193  *
194  * Its use is to make sure the service thread has seen the state of the task
195  * before deleting it.
196  */
197 LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status
198 lws_threadpool_task_status_wsi(struct lws *wsi,
199 			       struct lws_threadpool_task **task, void **user);
200 
201 /**
202  * lws_threadpool_task_sync() - Indicate to a stalled task it may continue
203  *
204  * \param task: the task to unblock
205  * \param stop: 0 = run after unblock, 1 = when he unblocks, stop him
206  *
207  * Inform the task that the service thread has finished with the shared data
208  * and that the task, if blocked in LWS_TP_RETURN_SYNC, may continue.
209  *
210  * If the lws service context determined that the task must be aborted, it
211  * should still call this but with stop = 1, causing the task to finish.
212  */
213 LWS_VISIBLE LWS_EXTERN void
214 lws_threadpool_task_sync(struct lws_threadpool_task *task, int stop);
215 
216 /**
217  * lws_threadpool_dump() - dump the state of a threadpool to the log
218  *
219  * \param tp: The threadpool to dump
220  *
221  * This locks the threadpool and then dumps the pending queue, the worker
222  * threads and the done queue, together with time information for how long
223  * the tasks have been in their current state, how long they have occupied a
224  * thread, etc.
225  *
226  * This only does anything on lws builds with CMAKE_BUILD_TYPE=DEBUG, otherwise
227  * while it still exists, it's a NOP.
228  */
229 
230 LWS_VISIBLE LWS_EXTERN void
231 lws_threadpool_dump(struct lws_threadpool *tp);
232 //@}
233