xref: /aosp_15_r20/external/libwebsockets/lib/misc/threadpool/README.md (revision 1c60b9aca93fdbc9b5f19b2d2194c91294b22281)
1*1c60b9acSAndroid Build Coastguard Worker## Threadpool
2*1c60b9acSAndroid Build Coastguard Worker
3*1c60b9acSAndroid Build Coastguard Worker### Overview
4*1c60b9acSAndroid Build Coastguard Worker
5*1c60b9acSAndroid Build Coastguard Worker![overview](/doc-assets/threadpool.svg)
6*1c60b9acSAndroid Build Coastguard Worker
7*1c60b9acSAndroid Build Coastguard WorkerAn api that lets you create a pool of worker threads, and a queue of tasks that
8*1c60b9acSAndroid Build Coastguard Workerare bound to a wsi.  Tasks in their own thread  synchronize communication to the
9*1c60b9acSAndroid Build Coastguard Workerlws service thread of the wsi via `LWS_CALLBACK_SERVER_WRITEABLE` and friends.
10*1c60b9acSAndroid Build Coastguard Worker
11*1c60b9acSAndroid Build Coastguard WorkerTasks can produce some output, then return that they want to "sync" with the
12*1c60b9acSAndroid Build Coastguard Workerservice thread.  That causes a `LWS_CALLBACK_SERVER_WRITEABLE` in the service
13*1c60b9acSAndroid Build Coastguard Workerthread context, where the output can be consumed, and the task told to continue,
14*1c60b9acSAndroid Build Coastguard Workeror completed tasks be reaped.
15*1c60b9acSAndroid Build Coastguard Worker
16*1c60b9acSAndroid Build Coastguard WorkerALL of the details related to thread synchronization and an associated wsi in
17*1c60b9acSAndroid Build Coastguard Workerthe lws service thread context are handled by the threadpool api, without needing
18*1c60b9acSAndroid Build Coastguard Workerany pthreads in user code.
19*1c60b9acSAndroid Build Coastguard Worker
20*1c60b9acSAndroid Build Coastguard Worker### Example
21*1c60b9acSAndroid Build Coastguard Worker
22*1c60b9acSAndroid Build Coastguard Workerhttps://libwebsockets.org/git/libwebsockets/tree/minimal-examples/ws-server/minimal-ws-server-threadpool
23*1c60b9acSAndroid Build Coastguard Worker
24*1c60b9acSAndroid Build Coastguard Worker### Lifecycle considerations
25*1c60b9acSAndroid Build Coastguard Worker
26*1c60b9acSAndroid Build Coastguard Worker#### Tasks vs wsi
27*1c60b9acSAndroid Build Coastguard Worker
28*1c60b9acSAndroid Build Coastguard WorkerAlthough all tasks start out as being associated to a wsi, in fact the lifetime
29*1c60b9acSAndroid Build Coastguard Workerof a task and that of the wsi are not necessarily linked.
30*1c60b9acSAndroid Build Coastguard Worker
31*1c60b9acSAndroid Build Coastguard WorkerYou may start a long task, eg, that runs atomically in its thread for 30s, and
32*1c60b9acSAndroid Build Coastguard Workerat any time the client may close the connection, eg, close a browser window.
33*1c60b9acSAndroid Build Coastguard Worker
34*1c60b9acSAndroid Build Coastguard WorkerThere are arrangements that a task can "check in" periodically with lws to see
35*1c60b9acSAndroid Build Coastguard Workerif it has been asked to stop, allowing the task lifetime to be related to the
36*1c60b9acSAndroid Build Coastguard Workerwsi lifetime somewhat, but some tasks are going to be atomic and longlived.
37*1c60b9acSAndroid Build Coastguard Worker
38*1c60b9acSAndroid Build Coastguard WorkerFor that reason, at wsi close an ongoing task can detach from the wsi and
39*1c60b9acSAndroid Build Coastguard Workercontinue until it ends or understands it has been asked to stop.  To make
40*1c60b9acSAndroid Build Coastguard Workerthat work, the task is created with a `cleanup` callback that performs any
41*1c60b9acSAndroid Build Coastguard Workerfreeing independent of still having a wsi around to do it... the task takes over
42*1c60b9acSAndroid Build Coastguard Workerresponsibility to free the user pointer on destruction when the task is created.
43*1c60b9acSAndroid Build Coastguard Worker
44*1c60b9acSAndroid Build Coastguard Worker![Threadpool States](/doc-assets/threadpool-states.svg)
45*1c60b9acSAndroid Build Coastguard Worker
46*1c60b9acSAndroid Build Coastguard Worker#### Reaping completed tasks
47*1c60b9acSAndroid Build Coastguard Worker
48*1c60b9acSAndroid Build Coastguard WorkerOnce created, although tasks may run asynchronously, the task itself does not
49*1c60b9acSAndroid Build Coastguard Workerget destroyed on completion but added to a "done queue".  Only when the lws
50*1c60b9acSAndroid Build Coastguard Workerservice thread context queries the task state with `lws_threadpool_task_status()`
51*1c60b9acSAndroid Build Coastguard Workermay the task be reaped and memory freed.
52*1c60b9acSAndroid Build Coastguard Worker
53*1c60b9acSAndroid Build Coastguard WorkerThis is analogous to unix processes and `wait()`.
54*1c60b9acSAndroid Build Coastguard Worker
55*1c60b9acSAndroid Build Coastguard WorkerIf a task became detached from its wsi, then joining the done queue is enough
56*1c60b9acSAndroid Build Coastguard Workerto get the task reaped, since there's nobody left any more to synchronize the
57*1c60b9acSAndroid Build Coastguard Workerreaping with.
58*1c60b9acSAndroid Build Coastguard Worker
59*1c60b9acSAndroid Build Coastguard Worker### User interface
60*1c60b9acSAndroid Build Coastguard Worker
61*1c60b9acSAndroid Build Coastguard WorkerThe api is declared at https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-threadpool.h
62*1c60b9acSAndroid Build Coastguard Worker
63*1c60b9acSAndroid Build Coastguard Worker#### Threadpool creation / destruction
64*1c60b9acSAndroid Build Coastguard Worker
65*1c60b9acSAndroid Build Coastguard WorkerThe threadpool should be created at program or vhost init using
66*1c60b9acSAndroid Build Coastguard Worker`lws_threadpool_create()` and destroyed on exit or vhost destruction using
67*1c60b9acSAndroid Build Coastguard Workerfirst `lws_threadpool_finish()` and then `lws_threadpool_destroy()`.
68*1c60b9acSAndroid Build Coastguard Worker
69*1c60b9acSAndroid Build Coastguard WorkerThreadpools should be named, varargs are provided on the create function
70*1c60b9acSAndroid Build Coastguard Workerto facilite eg, naming the threadpool by the vhost it's associated with.
71*1c60b9acSAndroid Build Coastguard Worker
72*1c60b9acSAndroid Build Coastguard WorkerThreadpool creation takes an args struct with the following members:
73*1c60b9acSAndroid Build Coastguard Worker
74*1c60b9acSAndroid Build Coastguard WorkerMember|function
75*1c60b9acSAndroid Build Coastguard Worker---|---
76*1c60b9acSAndroid Build Coastguard Workerthreads|The maxiumum number of independent threads in the pool
77*1c60b9acSAndroid Build Coastguard Workermax_queue_depth|The maximum number of tasks allowed to wait for a place in the pool
78*1c60b9acSAndroid Build Coastguard Worker
79*1c60b9acSAndroid Build Coastguard Worker#### Task creation / destruction
80*1c60b9acSAndroid Build Coastguard Worker
81*1c60b9acSAndroid Build Coastguard WorkerTasks are created and queued using `lws_threadpool_enqueue()`, this takes an
82*1c60b9acSAndroid Build Coastguard Workerargs struct with the following members
83*1c60b9acSAndroid Build Coastguard Worker
84*1c60b9acSAndroid Build Coastguard WorkerMember|function
85*1c60b9acSAndroid Build Coastguard Worker---|---
86*1c60b9acSAndroid Build Coastguard Workerwsi|The wsi the task is initially associated with
87*1c60b9acSAndroid Build Coastguard Workeruser|An opaque user-private pointer used for communication with the lws service thread and private state / data
88*1c60b9acSAndroid Build Coastguard Workertask|A pointer to the function that will run in the pool thread
89*1c60b9acSAndroid Build Coastguard Workercleanup|A pointer to a function that will clean up finished or stopped tasks (perhaps freeing user)
90*1c60b9acSAndroid Build Coastguard Worker
91*1c60b9acSAndroid Build Coastguard WorkerTasks also should have a name, the creation function again provides varargs
92*1c60b9acSAndroid Build Coastguard Workerto simplify naming the task with string elements related to who started it
93*1c60b9acSAndroid Build Coastguard Workerand why.
94*1c60b9acSAndroid Build Coastguard Worker
95*1c60b9acSAndroid Build Coastguard Worker#### The task function itself
96*1c60b9acSAndroid Build Coastguard Worker
97*1c60b9acSAndroid Build Coastguard WorkerThe task function receives the task user pointer and the task state.  The
98*1c60b9acSAndroid Build Coastguard Workerpossible task states are
99*1c60b9acSAndroid Build Coastguard Worker
100*1c60b9acSAndroid Build Coastguard WorkerState|Meaning
101*1c60b9acSAndroid Build Coastguard Worker---|---
102*1c60b9acSAndroid Build Coastguard WorkerLWS_TP_STATUS_QUEUED|Task is still waiting for a pool thread
103*1c60b9acSAndroid Build Coastguard WorkerLWS_TP_STATUS_RUNNING|Task is supposed to do its work
104*1c60b9acSAndroid Build Coastguard WorkerLWS_TP_STATUS_SYNCING|Task is blocked waiting for sync from lws service thread
105*1c60b9acSAndroid Build Coastguard WorkerLWS_TP_STATUS_STOPPING|Task has been asked to stop but didn't stop yet
106*1c60b9acSAndroid Build Coastguard WorkerLWS_TP_STATUS_FINISHED|Task has reported it has completed
107*1c60b9acSAndroid Build Coastguard WorkerLWS_TP_STATUS_STOPPED|Task has aborted
108*1c60b9acSAndroid Build Coastguard Worker
109*1c60b9acSAndroid Build Coastguard WorkerThe task function will only be told `LWS_TP_STATUS_RUNNING` or
110*1c60b9acSAndroid Build Coastguard Worker`LWS_TP_STATUS_STOPPING` in its status argument... RUNNING means continue with the
111*1c60b9acSAndroid Build Coastguard Workeruser task and STOPPING means clean up and return `LWS_TP_RETURN_STOPPED`.
112*1c60b9acSAndroid Build Coastguard Worker
113*1c60b9acSAndroid Build Coastguard WorkerIf possible every 100ms or so the task should return `LWS_TP_RETURN_CHECKING_IN`
114*1c60b9acSAndroid Build Coastguard Workerto allow lws to inform it reasonably quickly that it has been asked to stop
115*1c60b9acSAndroid Build Coastguard Worker(eg, because the related wsi has closed), or if it can continue.  If not
116*1c60b9acSAndroid Build Coastguard Workerpossible, it's okay but eg exiting the application may experience delays
117*1c60b9acSAndroid Build Coastguard Workeruntil the running task finishes, and since the wsi may have gone, the work
118*1c60b9acSAndroid Build Coastguard Workeris wasted.
119*1c60b9acSAndroid Build Coastguard Worker
120*1c60b9acSAndroid Build Coastguard WorkerThe task function may return one of
121*1c60b9acSAndroid Build Coastguard Worker
122*1c60b9acSAndroid Build Coastguard WorkerReturn|Meaning
123*1c60b9acSAndroid Build Coastguard Worker---|---
124*1c60b9acSAndroid Build Coastguard WorkerLWS_TP_RETURN_CHECKING_IN|Still wants to run, but confirming nobody asked him to stop.  Will be called again immediately with `LWS_TP_STATUS_RUNNING` or `LWS_TP_STATUS_STOPPING`
125*1c60b9acSAndroid Build Coastguard WorkerLWS_TP_RETURN_SYNC|Task wants to trigger a WRITABLE callback and block until lws service thread restarts it with `lws_threadpool_task_sync()`
126*1c60b9acSAndroid Build Coastguard WorkerLWS_TP_RETURN_FINISHED|Task has finished, successfully as far as it goes
127*1c60b9acSAndroid Build Coastguard WorkerLWS_TP_RETURN_STOPPED|Task has finished, aborting in response to a request to stop
128*1c60b9acSAndroid Build Coastguard Worker
129*1c60b9acSAndroid Build Coastguard WorkerThe SYNC or CHECKING_IN return may also have a flag `LWS_TP_RETURN_FLAG_OUTLIVE`
130*1c60b9acSAndroid Build Coastguard Workerapplied to it, which indicates to threadpool that this task wishes to remain
131*1c60b9acSAndroid Build Coastguard Workerunstopped after the wsi closes.  This is useful in the case where the task
132*1c60b9acSAndroid Build Coastguard Workerunderstands it will take a long time to complete, and wants to return a
133*1c60b9acSAndroid Build Coastguard Workercomplete status and maybe close the connection, perhaps with a token identifying
134*1c60b9acSAndroid Build Coastguard Workerthe task.  The task can then be monitored separately by using the token.
135*1c60b9acSAndroid Build Coastguard Worker
136*1c60b9acSAndroid Build Coastguard Worker#### Synchronizing
137*1c60b9acSAndroid Build Coastguard Worker
138*1c60b9acSAndroid Build Coastguard WorkerThe task can choose to "SYNC" with the lws service thread, in other words
139*1c60b9acSAndroid Build Coastguard Workercause a WRITABLE callback on the associated wsi in the lws service thread
140*1c60b9acSAndroid Build Coastguard Workercontext and block itself until it hears back from there via
141*1c60b9acSAndroid Build Coastguard Worker`lws_threadpool_task_sync()` to resume the task.
142*1c60b9acSAndroid Build Coastguard Worker
143*1c60b9acSAndroid Build Coastguard WorkerThis is typically used when, eg, the task has filled its buffer, or ringbuffer,
144*1c60b9acSAndroid Build Coastguard Workerand needs to pause operations until what's done has been sent and some buffer
145*1c60b9acSAndroid Build Coastguard Workerspace is open again.
146*1c60b9acSAndroid Build Coastguard Worker
147*1c60b9acSAndroid Build Coastguard WorkerIn the WRITABLE callback, in lws service thread context, the buffer can be
148*1c60b9acSAndroid Build Coastguard Workersent with `lws_write()` and then `lws_threadpool_task_sync()` to allow the task
149*1c60b9acSAndroid Build Coastguard Workerto fill another buffer and continue that way.
150*1c60b9acSAndroid Build Coastguard Worker
151*1c60b9acSAndroid Build Coastguard WorkerIf the WRITABLE callback determines that the task should stop, it can just call
152*1c60b9acSAndroid Build Coastguard Worker`lws_threadpool_task_sync()` with the second argument as 1, to force the task
153*1c60b9acSAndroid Build Coastguard Workerto stop immediately after it resumes.
154*1c60b9acSAndroid Build Coastguard Worker
155*1c60b9acSAndroid Build Coastguard Worker#### The cleanup function
156*1c60b9acSAndroid Build Coastguard Worker
157*1c60b9acSAndroid Build Coastguard WorkerWhen a finished task is reaped, or a task that become detached from its initial
158*1c60b9acSAndroid Build Coastguard Workerwsi completes or is stopped, it calls the `.cleanup` function defined in the
159*1c60b9acSAndroid Build Coastguard Workertask creation args struct to free anything related to the user pointer.
160*1c60b9acSAndroid Build Coastguard Worker
161*1c60b9acSAndroid Build Coastguard WorkerWith threadpool, responsibility for freeing allocations used by the task belongs
162*1c60b9acSAndroid Build Coastguard Workerstrictly with the task, via the `.cleanup` function, once the task has been
163*1c60b9acSAndroid Build Coastguard Workerenqueued.  That's different from a typical non-threadpool protocol where the
164*1c60b9acSAndroid Build Coastguard Workerwsi lifecycle controls deallocation.  This reflects the fact that the task
165*1c60b9acSAndroid Build Coastguard Workermay outlive the wsi.
166*1c60b9acSAndroid Build Coastguard Worker
167*1c60b9acSAndroid Build Coastguard Worker#### Protecting against WRITABLE and / or SYNC duplication
168*1c60b9acSAndroid Build Coastguard Worker
169*1c60b9acSAndroid Build Coastguard WorkerCare should be taken than data prepared by the task thread in the user priv
170*1c60b9acSAndroid Build Coastguard Workermemory should only be sent once.  For example, after sending data from a user
171*1c60b9acSAndroid Build Coastguard Workerpriv buffer of a given length stored in the priv, zero down the length.
172*1c60b9acSAndroid Build Coastguard Worker
173*1c60b9acSAndroid Build Coastguard WorkerTask execution and the SYNC writable callbacks are mutually exclusive, so there
174*1c60b9acSAndroid Build Coastguard Workeris no danger of collision between the task thread and the lws service thread if
175*1c60b9acSAndroid Build Coastguard Workerthe reason for the callback is a SYNC operation from the task thread.
176*1c60b9acSAndroid Build Coastguard Worker
177*1c60b9acSAndroid Build Coastguard Worker### Thread overcommit
178*1c60b9acSAndroid Build Coastguard Worker
179*1c60b9acSAndroid Build Coastguard WorkerIf the tasks running on the threads are ultimately network-bound for all or some
180*1c60b9acSAndroid Build Coastguard Workerof their processing (via the SYNC with the WRITEABLE callback), it's possible
181*1c60b9acSAndroid Build Coastguard Workerto overcommit the number of threads in the pool compared to the number of
182*1c60b9acSAndroid Build Coastguard Workerthreads the processor has in hardware to get better occupancy in the CPU.
183