asyncresponses.inc (19104B)
1 The previous chapter left one question open. A content reader that 2 has no data ready may return @code{0}, but doing so turns the daemon 3 into a busy loop, and the honest answer---suspend the connection---was 4 only sketched there. This chapter builds the thing itself: a server 5 whose slow work happens in threads of its own, whose connections are 6 parked while that work is under way, and which is driven by an 7 external @code{select()} loop so that the application stays in charge 8 of its own event handling. 9 10 The combination is a common one. Most requests are cheap and can be 11 answered on the spot; a few of them start a report, a backup or a 12 query that runs for seconds. Handling the slow ones inline would 13 block every other client for as long as they take. Giving every 14 connection its own thread---@code{MHD_USE_THREAD_PER_CONNECTION}---is 15 no answer either, both because it scales badly and because suspending 16 is not available in that mode at all. What is wanted is a single 17 event loop that never blocks, plus a worker per slow job. 18 19 20 @heading Who may call what 21 22 Almost every difficulty in this design comes from one rule, so it is 23 worth stating before any code: 24 25 @table @asis 26 @item @code{MHD_queue_response()} 27 Must be called from the thread that runs the daemon, from inside the 28 access handler. A worker thread must never queue a response. 29 30 @item @code{MHD_suspend_connection()} 31 May only be called from the access handler or from a content reader 32 callback---that is, from @emph{MHD}'s own thread, while @emph{MHD} is 33 calling into the application. 34 35 @item @code{MHD_resume_connection()} 36 May be called from any thread at any time, and this is the exception 37 that makes the whole design work. It is the only @emph{MHD} function 38 a worker thread ever needs. 39 @end table 40 41 @noindent 42 So the split is: the worker computes and, when it has something, 43 resumes. Everything that touches the connection or the response 44 happens on the daemon's thread, where @emph{MHD} calls the application 45 back. The worker and the daemon share exactly one object, a 46 per-request context, and the only thing the worker does with the 47 connection handle it holds is pass it to 48 @code{MHD_resume_connection()}. 49 50 The daemon has to be started with @code{MHD_ALLOW_SUSPEND_RESUME}; both 51 suspend and resume call @code{MHD_PANIC()} and abort the process 52 otherwise. Note also that suspending does not work with a thread pool: 53 @code{MHD_resume_connection()} asserts that the daemon has none. 54 55 56 @heading The event loop 57 58 Leaving out @code{MHD_USE_INTERNAL_POLLING_THREAD} puts @emph{MHD} 59 into external polling mode: it starts no thread of its own and does 60 nothing until the application asks it to. The loop is the usual one, 61 and the example adds its own descriptor to the very same sets---there 62 is nothing special about @emph{MHD}'s: 63 64 @verbatim 65 FD_ZERO (&rs); FD_ZERO (&ws); FD_ZERO (&es); 66 max = 0; 67 if (MHD_YES != MHD_get_fdset (daemon, &rs, &ws, &es, &max)) 68 break; /* fatal internal error */ 69 FD_SET (wake_pipe[0], &rs); /* our own wakeup pipe */ 70 if (max < wake_pipe[0]) 71 max = wake_pipe[0]; 72 73 if (MHD_YES == MHD_get_timeout64 (daemon, &mhd_timeout)) 74 { ...fill tv...; tvp = &tv; } 75 else 76 tvp = NULL; /* nothing to wait for */ 77 78 select ((int) max + 1, &rs, &ws, &es, tvp); 79 MHD_run_from_select (daemon, &rs, &ws, &es); 80 @end verbatim 81 @noindent 82 83 The interesting branch is the one that sets @code{tvp} to @code{NULL}. 84 @code{MHD_get_timeout64()} returns @code{MHD_NO} when @emph{MHD} has 85 nothing whatsoever to wait for, and with every connection suspended 86 that is precisely the situation: suspended connections do not time 87 out, so there is no deadline to wake up for. The loop then blocks in 88 @code{select()} indefinitely. Whether the server works at all depends 89 on something waking it up. 90 91 92 @heading Waking the loop up 93 94 That something is @emph{MHD}'s inter-thread communication channel. 95 @code{MHD_ALLOW_SUSPEND_RESUME} is defined as 96 @code{8192 | MHD_USE_ITC}, so requesting suspend support requests the 97 ITC as well; @code{MHD_get_fdset()} puts the reading end of that 98 channel into the read set, ahead of everything else; and 99 @code{MHD_resume_connection()} writes a byte to it. A worker thread 100 that resumes a connection therefore breaks the main thread out of 101 @code{select()}, even though the two share nothing but the daemon 102 handle. 103 104 This deserves emphasis because the header file is misleading about it: 105 the comment on @code{MHD_USE_ITC} says the flag is ignored with 106 external polling. That is true for the flag in isolation, but not for 107 the channel: @code{MHD_ALLOW_SUSPEND_RESUME} creates it regardless of 108 the polling mode, and in external mode it is the application's own 109 @code{select()} that watches it. Without that, a resumed connection 110 would sit untouched until some unrelated event happened to wake the 111 loop. 112 113 There is no race to guard against here. If the worker resumes in the 114 window between @code{MHD_get_fdset()} and @code{select()}, the byte is 115 already in the channel when @code{select()} is entered, and 116 @code{select()} returns immediately---the channel is level triggered, 117 so a wakeup cannot be missed. The same property makes it safe for a 118 worker to resume a connection that has not finished suspending yet: 119 @emph{MHD} remembers the request and cancels the pending suspension 120 instead of parking the connection. Neither side needs to know what 121 the other is doing. 122 123 124 @heading Answering in one piece 125 126 The simplest of the two patterns is for an answer that is only useful 127 as a whole---a generated report, the result of a query. Nothing can be 128 sent before it is finished, so the connection is suspended in the 129 access handler and the response is queued when @emph{MHD} comes back: 130 131 @verbatim 132 static enum MHD_Result 133 handle_slow (struct MHD_Connection *connection, void **req_cls) 134 { 135 struct Job *job = *req_cls; 136 137 if (NULL == job) 138 { 139 /* First call for this request: start the work and ask MHD to 140 come back to us. */ 141 job = job_create (connection, 0); 142 if (NULL == job) 143 return MHD_NO; 144 if (MHD_NO == job_start (job)) 145 { job_unref (job); job_unref (job); return MHD_NO; } 146 *req_cls = job; 147 return MHD_YES; 148 } 149 pthread_mutex_lock (&job->lock); 150 if (0 == job->finished) 151 { 152 job->suspended = 1; 153 MHD_suspend_connection (connection); 154 pthread_mutex_unlock (&job->lock); 155 return MHD_YES; 156 } 157 ...copy the result out from under the lock... 158 pthread_mutex_unlock (&job->lock); 159 ...MHD_create_response_from_buffer_copy() and MHD_queue_response()... 160 } 161 @end verbatim 162 @noindent 163 164 The control flow is worth following, because it is not obvious that it 165 terminates. @emph{MHD} calls the access handler for a request more 166 than once; the first call is the one where @code{*req_cls} is still 167 @code{NULL}, and returning @code{MHD_YES} without queueing anything 168 tells @emph{MHD} to carry on. On the second call the worker is 169 running but not finished, so the connection is suspended and the 170 handler returns again. When the worker resumes the connection, 171 @emph{MHD} calls the handler a third time---on its own thread, with 172 the same @code{*req_cls}---and this time @code{finished} is set and the 173 response is queued. 174 175 That third call is the answer to the question this pattern exists for. 176 The worker never queues anything; it merely makes the daemon call the 177 application back at a moment when the answer is ready. 178 179 180 @heading Answering as it is produced 181 182 The second pattern is the more useful one when the answer is long, and 183 it is what makes a browser show results as they appear rather than 184 after a long blank pause. Here the response is queued 185 @emph{immediately}, with a content reader and an unknown size, so the 186 headers go out at once and the body follows as the worker fills it in: 187 188 @verbatim 189 job = job_create (connection, 1); 190 response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 191 IO_BLOCK_SIZE, 192 &stream_reader, 193 job, 194 &stream_done); 195 ... 196 MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, 197 "text/event-stream"); 198 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 199 MHD_destroy_response (response); 200 @end verbatim 201 @noindent 202 203 Because the size is unknown, @emph{MHD} uses chunked transfer encoding, 204 and every piece the content reader hands back is framed as its own 205 chunk and goes out on the wire straight away. The content reader is 206 where suspending happens: 207 208 @verbatim 209 static ssize_t 210 stream_reader (void *cls, uint64_t pos, char *buf, size_t max) 211 { 212 struct Job *job = cls; 213 size_t ready; 214 215 pthread_mutex_lock (&job->lock); 216 if (job->off == job->fill) 217 { 218 job->off = 0; 219 job->fill = 0; 220 if (0 != job->finished) 221 { 222 pthread_mutex_unlock (&job->lock); 223 return MHD_CONTENT_READER_END_OF_STREAM; 224 } 225 /* Nothing to send yet. Take the connection out of the event 226 loop; the worker will put it back in. */ 227 job->suspended = 1; 228 MHD_suspend_connection (job->connection); 229 pthread_mutex_unlock (&job->lock); 230 return 0; 231 } 232 ready = job->fill - job->off; 233 if (ready > max) 234 ready = max; 235 memcpy (buf, &job->payload[job->off], ready); 236 job->off += ready; 237 pthread_mutex_unlock (&job->lock); 238 return (ssize_t) ready; 239 } 240 @end verbatim 241 @noindent 242 243 The return value is still @code{0}, exactly as in the busy-waiting 244 version the previous chapter warned about. The difference is the line 245 above it. Suspending removes the connection from the event loop, so 246 @emph{MHD} has no reason to come back until the worker says so, and 247 the process goes to sleep. The example ships with a test that measures 248 this: streaming a document that takes one second to produce costs the 249 server no measurable CPU time at all while the connection is suspended, 250 and very nearly a full second---one core, saturated---when the 251 @code{MHD_suspend_connection()} call is removed and only the 252 @code{return 0} is left. The bytes that arrive are identical in both 253 cases, which is what makes this such an easy mistake to keep. 254 255 256 @heading The shared state 257 258 Everything the two threads share sits in one structure per request: 259 260 @verbatim 261 struct Job 262 { 263 pthread_mutex_t lock; 264 pthread_cond_t cond; 265 struct MHD_Connection *connection; 266 char payload[MAX_PAYLOAD]; 267 size_t fill; 268 size_t off; 269 unsigned int rc; 270 int finished; 271 int suspended; 272 int abandoned; 273 ... 274 }; 275 @end verbatim 276 @noindent 277 278 There is no registry of pending requests involved in answering them, 279 and no lookup: the pointer travels in @code{*req_cls} for the first 280 pattern and in the content reader's @code{crc_cls} for the second, and 281 both the daemon and the worker are handed it directly. A shared list 282 of all jobs is still useful, but only for shutting down in an orderly 283 fashion, and it is protected by a lock of its own that is always taken 284 before any job's lock, never after. 285 286 The structure is reference counted, with one reference held by 287 @emph{MHD} and one by the worker. The @emph{MHD} side lets go in the 288 free callback of the response for a streamed answer, and in the 289 @code{MHD_OPTION_NOTIFY_COMPLETED} callback for the other pattern; the 290 worker lets go when it returns. Dropping the last reference does not 291 free the job, though, because the worker still has to be joined and 292 only the main thread may do that. The main loop therefore ends with a 293 sweep that joins and frees whatever has no references left. 294 295 296 @heading When the client goes away 297 298 This is the part that is easy to get wrong, because it does not happen 299 during development and does happen constantly in production. A client 300 that closes the connection halfway through a download leaves a worker 301 running with a pointer to a connection @emph{MHD} is about to free. 302 303 The two callbacks that tell the application a request is over---the 304 response's free callback and @code{MHD_OPTION_NOTIFY_COMPLETED}---are 305 therefore where the worker is cut loose: 306 307 @verbatim 308 static void 309 job_detach (struct Job *job) 310 { 311 pthread_mutex_lock (&job->lock); 312 job->abandoned = 1; 313 job->connection = NULL; 314 pthread_cond_signal (&job->cond); 315 pthread_mutex_unlock (&job->lock); 316 job_unref (job); 317 } 318 @end verbatim 319 @noindent 320 321 and the worker checks that flag before every resume: 322 323 @verbatim 324 static void 325 job_resume_locked (struct Job *job) 326 { 327 if ( (0 == job->suspended) || (0 != job->abandoned) ) 328 return; 329 job->suspended = 0; 330 MHD_resume_connection (job->connection); 331 } 332 @end verbatim 333 @noindent 334 335 Note that this function is called with the job's lock @emph{held}, and 336 calls into @emph{MHD} without releasing it. That is deliberate and it 337 is what makes the pattern safe. @emph{MHD} destroys the connection 338 only after the completion callback has returned, and that callback 339 cannot run while the worker holds the lock; so for as long as the 340 worker is inside @code{MHD_resume_connection()}, the connection it 341 passes is guaranteed to still exist. Dropping the lock first and 342 resuming afterwards would open exactly the window this is meant to 343 close. 344 345 The reverse order never occurs: @emph{MHD} invokes the free and 346 completion callbacks without holding the internal locks that 347 @code{MHD_resume_connection()} acquires, so there is no way for the 348 two to deadlock. 349 350 The condition variable in the same function is a convenience rather 351 than a necessity. Signalling it wakes a worker that is asleep between 352 steps, so that the work stops within microseconds of the client 353 disappearing instead of running to completion for nobody. 354 355 356 @heading Shutting down 357 358 Stopping a daemon that still has suspended connections is an API 359 violation, and a suspended connection is one that only a worker can 360 release. The order is therefore fixed: 361 362 @enumerate 363 @item 364 Stop handing out new jobs. 365 366 @item 367 Tell every worker to give up, and @emph{join} them. After this point 368 no connection can be suspended any more: the workers resume before 369 they exit, and the content reader only ever suspends while a worker is 370 running. 371 372 @item 373 Keep running the event loop until the connections have drained, so 374 that the last bytes actually reach the clients. 375 376 @item 377 @code{MHD_stop_daemon()}. 378 @end enumerate 379 380 @noindent 381 Skipping the join in step two is the tempting shortcut, and it is the 382 one that leaves a connection parked forever with nothing left alive to 383 wake it. 384 385 386 @heading Example code 387 388 The complete program is available as @code{asyncresponse.c}. It runs 389 one external @code{select()} loop and serves four URLs: 390 391 @table @code 392 @item / 393 A small page whose JavaScript opens the stream below and appends a row 394 for every event as it arrives. The rows visibly trickle in, one per 395 step, which is the most direct demonstration of what the chapter is 396 about. 397 398 @item /events 399 The stream: a response is queued at once, and the content reader 400 suspends between steps. The body is a sequence of server-sent events, 401 which needs no library on the browser side. 402 403 @item /slow 404 The other pattern: the connection is suspended in the access handler 405 and a complete answer is queued once the worker is done. 406 407 @item /fast 408 Answered on the spot. The page has a button that requests it and 409 reports the round trip time; it stays in the low milliseconds while 410 jobs are running, which is the point of suspending rather than 411 blocking. 412 @end table 413 414 @noindent 415 The program takes the port, the duration of a step in milliseconds and 416 the number of steps on the command line. Passing @code{0} as the port 417 makes the operating system choose a free one, which the program then 418 reports via @code{MHD_DAEMON_INFO_BIND_PORT}: 419 420 @verbatim 421 $ ./asyncresponse 0 250 20 422 Listening on port 44321 423 @end verbatim 424 @noindent 425 426 The accompanying @code{test_asyncresponse.c} starts exactly that 427 binary and checks over HTTP that @code{/slow} waits for its worker, 428 that the body of @code{/events} really arrives in pieces spread over 429 the lifetime of the request rather than in one burst at the end, that 430 @code{/fast} is still served promptly while jobs are parked, that a 431 client which hangs up in the middle takes neither the server nor the 432 worker with it, and that the process exits cleanly when it is signalled 433 while a connection is suspended. It also compares the server's CPU 434 time against the wall clock time of a transfer, which is the only one 435 of those checks that notices the difference between a suspended 436 connection and a busy-waiting one. 437 438 439 @heading Remarks 440 441 @emph{MHD} does not detect that a client has disconnected while a 442 connection is suspended, and the connection timeout does not run 443 either. A suspended connection stays suspended until somebody resumes 444 it, without exception. Any timeout on the work itself is therefore the 445 application's responsibility. 446 447 Suspended connections still count against the connection limits, both 448 the global one and the per-IP one. A server that parks connections for 449 minutes needs its limits sized for the number of jobs it expects to 450 have in flight, not for the number it expects to be transmitting. 451 452 If the client vanishes in the middle of a chunked response, the next 453 write fails and @emph{MHD} logs it---with 454 @code{MHD_USE_ERROR_LOG} enabled the example prints @code{Failed to 455 send the chunked response body ... The socket is no longer available 456 for sending}. That is the normal course of events for an aborted 457 download and not a sign of a problem. 458 459 Finally, none of this is specific to @code{select()}. The same 460 argument holds for @code{poll()} and for @code{epoll}, where the 461 daemon's descriptor can be obtained with 462 @code{MHD_DAEMON_INFO_EPOLL_FD} and added to an epoll set of the 463 application's own; @code{suspend_resume_epoll.c} among the 464 distribution's examples shows that variant. 465 466 467 @heading Exercises 468 469 @itemize @bullet 470 471 @item 472 Give the jobs a deadline. Since a suspended connection never times 473 out on its own, add a check to the main loop that resumes---and 474 answers with @code{503}---any job that has been running for too long, 475 and confirm with a step duration long enough to trigger it. 476 477 @item 478 Remove the @code{MHD_suspend_connection()} call from the content 479 reader, leaving only the @code{return 0}, and watch the server process 480 with @code{top} while a single client downloads the stream. Then put 481 it back and watch again. Both servers send the same bytes. 482 483 @item 484 Take the resume out of the worker's final block, so that a finished job 485 leaves its connection parked, and then stop the server while a job is 486 in flight. Watch what the shutdown does---and what @code{valgrind} 487 has to say about it---and work out which of the four steps above was 488 violated. 489 490 @item 491 Replace the per-request thread with a fixed pool of worker threads and 492 a queue of pending jobs. The reference counting and the 493 @code{abandoned} flag should not need to change at all; if they do, the 494 ownership rules were not as clean as they looked. 495 496 @item 497 Serve the stream to two browser windows at once and confirm from the 498 timestamps that the two jobs run concurrently and are interleaved by a 499 single-threaded event loop. 500 501 @end itemize