Skip to content

Commit e6d153f

Browse files
author
Sascha Schumann
committed
Support for handling multiple concurrent requests.
1 parent b327e65 commit e6d153f

File tree

1 file changed

+223
-5
lines changed

1 file changed

+223
-5
lines changed

sapi/thttpd/thttpd.c

Lines changed: 223 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#define bytes_sent bytes
12
/*
23
+----------------------------------------------------------------------+
34
| PHP version 4.0 |
@@ -34,14 +35,24 @@ typedef struct {
3435
void (*on_close)(int);
3536
} php_thttpd_globals;
3637

37-
static php_thttpd_globals thttpd_globals;
3838

39+
#ifdef ZTS
40+
static int thttpd_globals_id;
41+
#define TLS_D php_thttpd_globals *thttpd_context
42+
#define TLS_DC , TLS_D
43+
#define TLS_C thttpd_context
44+
#define TLS_CC , thttpd_context
45+
#define TG(v) (thttpd_context->v)
46+
#define TLS_FETCH() TLS_D = ts_resource(thttpd_globals_id)
47+
#else
48+
static php_thttpd_globals thttpd_globals;
3949
#define TLS_D
4050
#define TLS_DC
4151
#define TLS_C
4252
#define TLS_CC
4353
#define TG(v) (thttpd_globals.v)
4454
#define TLS_FETCH()
55+
#endif
4556

4657
static int sapi_thttpd_ub_write(const char *str, uint str_length)
4758
{
@@ -78,6 +89,7 @@ static int sapi_thttpd_send_headers(sapi_headers_struct *sapi_headers SLS_DC)
7889
zend_llist_position pos;
7990
sapi_header_struct *h;
8091
size_t len;
92+
TLS_FETCH();
8193

8294
if (!SG(sapi_headers).http_status_line) {
8395
snprintf(buf, 1023, "HTTP/1.0 %d Something\r\n", SG(sapi_headers).http_response_code);
@@ -194,7 +206,7 @@ static void sapi_thttpd_register_variables(zval *track_vars_array ELS_DC SLS_DC
194206

195207
#define CONDADD(name, field) \
196208
if (TG(hc)->field[0]) { \
197-
php_register_variable(#name, TG(hc)->field, track_vars_array ELS_CC PLS_C); \
209+
php_register_variable(#name, TG(hc)->field, track_vars_array ELS_CC PLS_CC); \
198210
}
199211

200212
CONDADD(HTTP_REFERER, referer);
@@ -212,7 +224,7 @@ static void sapi_thttpd_register_variables(zval *track_vars_array ELS_DC SLS_DC
212224
}
213225

214226
if (TG(hc)->authorization[0])
215-
php_register_variable("AUTH_TYPE", "Basic", track_vars_array ELS_CC PLS_C);
227+
php_register_variable("AUTH_TYPE", "Basic", track_vars_array ELS_CC PLS_CC);
216228
}
217229

218230
static sapi_module_struct thttpd_sapi_module = {
@@ -313,11 +325,187 @@ static void thttpd_request_dtor(TLS_D SLS_DC)
313325
free(SG(request_info).path_translated);
314326
}
315327

316-
off_t thttpd_php_request(httpd_conn *hc)
328+
#ifdef ZTS
329+
330+
#ifdef TSRM_ST
331+
#define thread_create_simple_detached(n) st_thread_create(n, NULL, 0, 0)
332+
#define thread_usleep(n) st_usleep(n)
333+
#define thread_exit() st_thread_exit(NULL)
334+
/* No preemption, simple operations are safe */
335+
#define thread_atomic_inc(n) (++n)
336+
#define thread_atomic_dec(n) (--n)
337+
#else
338+
#error No thread primitives available
339+
#endif
340+
341+
/* We might want to replace this with a STAILQ */
342+
typedef struct qreq {
343+
httpd_conn *hc;
344+
struct qreq *next;
345+
} qreq_t;
346+
347+
static MUTEX_T qr_lock;
348+
static qreq_t *queued_requests;
349+
static qreq_t *last_qr;
350+
static int nr_free_threads;
351+
static int nr_threads;
352+
static int max_threads = 50;
353+
354+
#define HANDLE_STRINGS() { \
355+
HANDLE_STR(encodedurl); \
356+
HANDLE_STR(decodedurl); \
357+
HANDLE_STR(origfilename); \
358+
HANDLE_STR(expnfilename); \
359+
HANDLE_STR(pathinfo); \
360+
HANDLE_STR(query); \
361+
HANDLE_STR(referer); \
362+
HANDLE_STR(useragent); \
363+
HANDLE_STR(accept); \
364+
HANDLE_STR(accepte); \
365+
HANDLE_STR(acceptl); \
366+
HANDLE_STR(cookie); \
367+
HANDLE_STR(contenttype); \
368+
HANDLE_STR(authorization); \
369+
HANDLE_STR(remoteuser); \
370+
}
371+
372+
static httpd_conn *duplicate_conn(httpd_conn *hc, httpd_conn *nhc)
373+
{
374+
memcpy(nhc, hc, sizeof(*nhc));
375+
376+
#define HANDLE_STR(m) nhc->m = nhc->m ? strdup(nhc->m) : NULL
377+
HANDLE_STRINGS();
378+
#undef HANDLE_STR
379+
380+
return nhc;
381+
}
382+
383+
static void destroy_conn(httpd_conn *hc)
384+
{
385+
#define HANDLE_STR(m) if (hc->m) free(hc->m)
386+
HANDLE_STRINGS();
387+
#undef HANDLE_STR
388+
}
389+
390+
static httpd_conn *dequeue_request(void)
391+
{
392+
httpd_conn *ret = NULL;
393+
qreq_t *m;
394+
395+
tsrm_mutex_lock(qr_lock);
396+
if (queued_requests) {
397+
m = queued_requests;
398+
ret = m->hc;
399+
if (!(queued_requests = m->next))
400+
last_qr = NULL;
401+
free(m);
402+
}
403+
tsrm_mutex_unlock(qr_lock);
404+
405+
return ret;
406+
}
407+
408+
static void *worker_thread(void *);
409+
410+
static void queue_request(httpd_conn *hc)
411+
{
412+
qreq_t *m;
413+
httpd_conn *nhc;
414+
415+
/* Mark as long-running request */
416+
hc->file_address = (char *) 1;
417+
418+
/*
419+
* We cannot synchronously revoke accesses to hc in the worker
420+
* thread, so we need to pass a copy of hc to the worker thread.
421+
*/
422+
nhc = malloc(sizeof *nhc);
423+
duplicate_conn(hc, nhc);
424+
425+
/* Allocate request queue container */
426+
m = malloc(sizeof *m);
427+
m->hc = nhc;
428+
m->next = NULL;
429+
430+
tsrm_mutex_lock(qr_lock);
431+
/* Create new threads when reaching a certain threshhold */
432+
if (nr_threads < max_threads && nr_free_threads < 2) {
433+
nr_threads++; /* protected by qr_lock */
434+
435+
thread_atomic_inc(nr_free_threads);
436+
thread_create_simple_detached(worker_thread);
437+
}
438+
/* Insert container into request queue */
439+
if (queued_requests)
440+
last_qr->next = m;
441+
else
442+
queued_requests = m;
443+
last_qr = m;
444+
tsrm_mutex_unlock(qr_lock);
445+
}
446+
447+
static off_t thttpd_real_php_request(httpd_conn *hc TLS_DC SLS_DC);
448+
449+
static void *worker_thread(void *dummy)
317450
{
451+
int do_work = 50;
452+
httpd_conn *hc;
318453
SLS_FETCH();
319454
TLS_FETCH();
320455

456+
while (do_work) {
457+
hc = dequeue_request();
458+
459+
if (!hc) {
460+
/* do_work--; */
461+
thread_usleep(500000);
462+
continue;
463+
}
464+
/* do_work = 50; */
465+
466+
thread_atomic_dec(nr_free_threads);
467+
468+
thttpd_real_php_request(hc TLS_CC SLS_CC);
469+
shutdown(hc->conn_fd, 0);
470+
destroy_conn(hc);
471+
free(hc);
472+
473+
thread_atomic_inc(nr_free_threads);
474+
}
475+
thread_atomic_dec(nr_free_threads);
476+
thread_atomic_dec(nr_threads);
477+
thread_exit();
478+
}
479+
480+
static void remove_dead_conn(int fd)
481+
{
482+
qreq_t *m, *prev = NULL;
483+
484+
tsrm_mutex_lock(qr_lock);
485+
m = queued_requests;
486+
while (m) {
487+
if (m->hc->conn_fd == fd) {
488+
if (prev)
489+
if (!(prev->next = m->next))
490+
last_qr = prev;
491+
else
492+
if (!(queued_requests = m->next))
493+
last_qr = NULL;
494+
destroy_conn(m->hc);
495+
free(m->hc);
496+
free(m);
497+
break;
498+
}
499+
prev = m;
500+
m = m->next;
501+
}
502+
tsrm_mutex_unlock(qr_lock);
503+
}
504+
505+
#endif
506+
507+
static off_t thttpd_real_php_request(httpd_conn *hc TLS_DC SLS_DC)
508+
{
321509
TG(hc) = hc;
322510
hc->bytes_sent = 0;
323511

@@ -330,37 +518,67 @@ off_t thttpd_php_request(httpd_conn *hc)
330518
return 0;
331519
}
332520

521+
off_t thttpd_php_request(httpd_conn *hc)
522+
{
523+
#ifdef ZTS
524+
queue_request(hc);
525+
#else
526+
SLS_FETCH();
527+
TLS_FETCH();
528+
return thttpd_real_php_request(hc TLS_CC SLS_CC);
529+
#endif
530+
}
531+
333532
void thttpd_register_on_close(void (*arg)(int))
334533
{
534+
TLS_FETCH();
335535
TG(on_close) = arg;
336536
}
337537

338538
void thttpd_closed_conn(int fd)
339539
{
540+
TLS_FETCH();
340541
if (TG(on_close)) TG(on_close)(fd);
341542
}
342543

343544
int thttpd_get_fd(void)
344545
{
546+
TLS_FETCH();
345547
return TG(hc)->conn_fd;
346548
}
347549

348550
void thttpd_set_dont_close(void)
349551
{
552+
TLS_FETCH();
350553
TG(hc)->file_address = (char *) 1;
351554
}
352555

556+
353557
void thttpd_php_init(void)
354558
{
559+
#ifdef ZTS
560+
tsrm_startup(1, 1, 0, NULL);
561+
thttpd_globals_id = ts_allocate_id(sizeof(php_thttpd_globals), NULL, NULL);
562+
qr_lock = tsrm_mutex_alloc();
563+
thttpd_register_on_close(remove_dead_conn);
564+
#endif
355565
sapi_startup(&thttpd_sapi_module);
356566
thttpd_sapi_module.startup(&thttpd_sapi_module);
357-
SG(server_context) = (void *) 1;
567+
{
568+
SLS_FETCH();
569+
SG(server_context) = (void *) 1;
570+
}
358571
}
359572

360573
void thttpd_php_shutdown(void)
361574
{
575+
SLS_FETCH();
576+
362577
if (SG(server_context) != NULL) {
363578
thttpd_sapi_module.shutdown(&thttpd_sapi_module);
364579
sapi_shutdown();
580+
#ifdef ZTS
581+
tsrm_shutdown();
582+
#endif
365583
}
366584
}

0 commit comments

Comments
 (0)