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 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 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