Jelajahi Sumber

H2O: Optimize the implementation further (#2356)

knewmanTE 8 tahun lalu
induk
melakukan
ac22e68d9a

+ 1 - 1
frameworks/C/h2o/setup.sh

@@ -25,7 +25,7 @@ run_curl()
 
 run_h2o_app()
 {
-	"$1/h2o_app" -a2 -f "$2/template/fortunes.mustache" -m8 "$3" "$4" \
+	"$1/h2o_app" -a1 -f "$2/template/fortunes.mustache" -m5 "$3" "$4" \
 		-d "host=$DBHOST dbname=hello_world user=benchmarkdbuser password=benchmarkdbpass" &
 }
 

+ 4 - 5
frameworks/C/h2o/src/database.c

@@ -353,8 +353,7 @@ static void start_database_connect(thread_context_t *ctx, db_conn_t *db_conn)
 			goto error;
 		}
 
-		const char * const conninfo =
-			ctx->global_data->config->db_host ? ctx->global_data->config->db_host : "";
+		const char * const conninfo = ctx->config->db_host ? ctx->config->db_host : "";
 
 		db_conn->conn = PQconnectStart(conninfo);
 
@@ -441,7 +440,7 @@ static void stop_database_write_polling(db_conn_t *db_conn)
 
 void connect_to_database(thread_context_t *ctx)
 {
-	for (size_t i = ctx->db_state.db_conn_num; i < ctx->global_data->config->max_db_conn_num; i++)
+	for (size_t i = ctx->db_state.db_conn_num; i < ctx->config->max_db_conn_num; i++)
 		start_database_connect(ctx, NULL);
 }
 
@@ -475,13 +474,13 @@ int execute_query(thread_context_t *ctx, db_query_param_t *param)
 		db_conn->param = param;
 		do_execute_query(db_conn);
 	}
-	else if (ctx->db_state.query_num < ctx->global_data->config->max_query_num) {
+	else if (ctx->db_state.query_num < ctx->config->max_query_num) {
 		param->l.next = NULL;
 		*ctx->db_state.queries.tail = &param->l;
 		ctx->db_state.queries.tail = &param->l.next;
 		ctx->db_state.query_num++;
 
-		if (ctx->db_state.db_conn_num < ctx->global_data->config->max_db_conn_num)
+		if (ctx->db_state.db_conn_num < ctx->config->max_db_conn_num)
 			start_database_connect(ctx, NULL);
 	}
 	else

+ 11 - 14
frameworks/C/h2o/src/event_loop.c

@@ -24,7 +24,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/epoll.h>
-#include <sys/syscall.h>
 
 #include "error.h"
 #include "event_loop.h"
@@ -57,7 +56,7 @@ static void accept_connection(h2o_socket_t *listener, const char *err)
 				sock->on_close.cb = on_close_connection;
 				sock->on_close.data = &ctx->event_loop.conn_num;
 				h2o_accept(&ctx->event_loop.h2o_accept_ctx, sock);
-			} while (++accepted < ctx->global_data->config->max_accept);
+			} while (++accepted < ctx->config->max_accept);
 		}
 	}
 }
@@ -96,11 +95,11 @@ static void process_messages(h2o_multithread_receiver_t *receiver, h2o_linklist_
 {
 	IGNORE_FUNCTION_PARAMETER(messages);
 
-	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
-	                                                      event_loop.h2o_receiver,
-	                                                      receiver);
+	global_thread_data_t * const global_thread_data = H2O_STRUCT_FROM_MEMBER(global_thread_data_t,
+	                                                                         h2o_receiver,
+	                                                                         receiver);
 
-	h2o_socket_read_stop(ctx->event_loop.h2o_socket);
+	h2o_socket_read_stop(global_thread_data->ctx->event_loop.h2o_socket);
 }
 
 static void shutdown_server(h2o_socket_t *listener, const char *err)
@@ -113,23 +112,20 @@ static void shutdown_server(h2o_socket_t *listener, const char *err)
 		ctx->global_data->shutdown = true;
 		h2o_socket_read_stop(ctx->event_loop.h2o_socket);
 
-		for (size_t i = 1; i < ctx->global_data->config->thread_num; i++)
-			h2o_multithread_send_message(&ctx[i].event_loop.h2o_receiver, NULL);
+		for (size_t i = 1; i < ctx->config->thread_num; i++)
+			h2o_multithread_send_message(&ctx->global_thread_data[i].h2o_receiver, NULL);
 	}
 }
 
 void event_loop(thread_context_t *ctx)
 {
-	ctx->tid = syscall(SYS_gettid);
-	ctx->random_seed = ctx->tid;
-
 	while (!ctx->global_data->shutdown || ctx->event_loop.conn_num)
 		h2o_evloop_run(ctx->event_loop.h2o_ctx.loop);
 }
 
-void free_event_loop(event_loop_t *event_loop)
+void free_event_loop(event_loop_t *event_loop, h2o_multithread_receiver_t *h2o_receiver)
 {
-	h2o_multithread_unregister_receiver(event_loop->h2o_ctx.queue, &event_loop->h2o_receiver);
+	h2o_multithread_unregister_receiver(event_loop->h2o_ctx.queue, h2o_receiver);
 	h2o_socket_close(event_loop->h2o_socket);
 	h2o_socket_close(event_loop->epoll_socket);
 	h2o_context_dispose(&event_loop->h2o_ctx);
@@ -137,6 +133,7 @@ void free_event_loop(event_loop_t *event_loop)
 
 void initialize_event_loop(bool is_main_thread,
                            global_data_t *global_data,
+                           h2o_multithread_receiver_t *h2o_receiver,
                            event_loop_t *loop)
 {
 	memset(loop, 0, sizeof(*loop));
@@ -165,7 +162,7 @@ void initialize_event_loop(bool is_main_thread,
 	loop->h2o_socket->data = loop;
 	h2o_socket_read_start(loop->h2o_socket, accept_connection);
 	h2o_multithread_register_receiver(loop->h2o_ctx.queue,
-	                                  &loop->h2o_receiver,
+	                                  h2o_receiver,
 	                                  process_messages);
 	// libh2o's event loop does not support write polling unless it
 	// controls sending the data as well, so do read polling on the

+ 2 - 2
frameworks/C/h2o/src/event_loop.h

@@ -35,13 +35,13 @@ typedef struct {
 	int epoll_fd;
 	h2o_accept_ctx_t h2o_accept_ctx;
 	h2o_context_t h2o_ctx;
-	h2o_multithread_receiver_t h2o_receiver;
 } event_loop_t;
 
 void event_loop(thread_context_t *ctx);
-void free_event_loop(event_loop_t *event_loop);
+void free_event_loop(event_loop_t *event_loop, h2o_multithread_receiver_t *h2o_receiver);
 void initialize_event_loop(bool is_main_thread,
                            global_data_t *global_data,
+                           h2o_multithread_receiver_t *h2o_receiver,
                            event_loop_t *loop);
 int start_write_polling(int fd,
                         void (**on_write_ready)(void *),

+ 37 - 28
frameworks/C/h2o/src/fortune.c

@@ -59,6 +59,7 @@ static uintmax_t add_iovec(mustache_api_t *api,
                            void *userdata,
                            const char *buffer,
                            uintmax_t buffer_size);
+static void cleanup_fortunes(void *data);
 static int compare_fortunes(const list_t *x, const list_t *y);
 static void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req);
 static list_t *get_sorted_sublist(list_t *head);
@@ -109,6 +110,22 @@ static uintmax_t add_iovec(mustache_api_t *api,
 	return ret;
 }
 
+static void cleanup_fortunes(void *data)
+{
+	fortune_ctx_t * const fortune_ctx = data;
+	const list_t *iter = fortune_ctx->result;
+
+	if (iter)
+		do {
+			const fortune_t * const fortune = H2O_STRUCT_FROM_MEMBER(fortune_t, l, iter);
+
+			if (fortune->data)
+				PQclear(fortune->data);
+
+			iter = iter->next;
+		} while (iter);
+}
+
 static int compare_fortunes(const list_t *x, const list_t *y)
 {
 	const fortune_t * const f1 = H2O_STRUCT_FROM_MEMBER(fortune_t, l, x);
@@ -142,12 +159,7 @@ static list_t *get_sorted_sublist(list_t *head)
 	if (head) {
 		head = head->next;
 
-		while (head && compare_fortunes(tail, head) < 0) {
-			tail = head;
-			head = head->next;
-		}
-
-		while (head && !compare_fortunes(tail, head)) {
+		while (head && compare_fortunes(tail, head) <= 0) {
 			tail = head;
 			head = head->next;
 		}
@@ -209,40 +221,33 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 		ret = SUCCESS;
 
 		for (size_t i = 0; i < num_rows; i++) {
-			const char * const message_data = PQgetvalue(result, i, 1);
-			h2o_iovec_t message = h2o_htmlescape(&fortune_ctx->req->pool,
-			                                     message_data,
-			                                     PQgetlength(result, i, 1));
-			const size_t id_len = PQgetlength(result, i, 0);
-			const size_t fortune_size = offsetof(fortune_t, data) + id_len +
-			                            (message_data == message.base ? message.len : 0);
 			fortune_t * const fortune = h2o_mem_alloc_pool(&fortune_ctx->req->pool,
-			                                               fortune_size);
+			                                               sizeof(*fortune));
 
 			if (fortune) {
-				memset(fortune, 0, offsetof(fortune_t, data));
-				memcpy(fortune->data, PQgetvalue(result, i, 0), id_len);
-				fortune->id.base = fortune->data;
-				fortune->id.len = id_len;
-
-				if (message_data == message.base) {
-					message.base = fortune->data + id_len;
-					memcpy(message.base, message_data, message.len);
-				}
-
-				fortune->message = message;
+				memset(fortune, 0, sizeof(*fortune));
+				fortune->id.base = PQgetvalue(result, i, 0);
+				fortune->id.len = PQgetlength(result, i, 0);
+				fortune->message = h2o_htmlescape(&fortune_ctx->req->pool,
+				                                  PQgetvalue(result, i, 1),
+				                                  PQgetlength(result, i, 1));
 				fortune->l.next = fortune_ctx->result;
 				fortune_ctx->result = &fortune->l;
 				fortune_ctx->num_result++;
+
+				if (!i)
+					fortune->data = result;
 			}
 			else {
 				send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
 				ret = DONE;
+
+				if (!i)
+					PQclear(result);
+
 				break;
 			}
 		}
-
-		PQclear(result);
 	}
 	else if (result) {
 		PQclear(result);
@@ -365,7 +370,9 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
 	                                                      event_loop.h2o_ctx,
 	                                                      req->conn->ctx);
-	fortune_ctx_t * const fortune_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune_ctx));
+	fortune_ctx_t * const fortune_ctx = h2o_mem_alloc_shared(&req->pool,
+	                                                         sizeof(*fortune_ctx),
+	                                                         cleanup_fortunes);
 
 	if (fortune_ctx) {
 		fortune_t * const fortune = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune));
@@ -390,6 +397,8 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 			if (execute_query(ctx, &fortune_ctx->param))
 				send_service_unavailable_error(DB_REQ_ERROR, req);
 		}
+		else
+			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 	}
 	else
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);

+ 1 - 1
frameworks/C/h2o/src/fortune.h

@@ -28,9 +28,9 @@
 
 typedef struct {
 	list_t l;
+	PGresult *data;
 	h2o_iovec_t id;
 	h2o_iovec_t message;
-	char data[];
 } fortune_t;
 
 int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req);

+ 25 - 10
frameworks/C/h2o/src/main.c

@@ -24,6 +24,7 @@
 #include <netdb.h>
 #include <signal.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -44,6 +45,7 @@
 #include "tls.h"
 #include "utility.h"
 
+#define DEFAULT_CACHE_LINE_SIZE 128
 #define DEFAULT_TCP_FASTOPEN_QUEUE_LEN 4096
 #define USAGE_MESSAGE \
 	"Usage:\n%s [-a <max connections accepted simultaneously>] [-b <bind address>] " \
@@ -63,8 +65,12 @@ static void setup_process(void);
 
 static void free_global_data(global_data_t *global_data)
 {
-	if (global_data->ctx)
-		free_thread_contexts(global_data);
+	if (global_data->global_thread_data) {
+		for (size_t i = 1; i < global_data->global_thread_data->config->thread_num; i++)
+			CHECK_ERROR(pthread_join, global_data->global_thread_data[i].thread, NULL);
+
+		free(global_data->global_thread_data);
+	}
 
 	if (global_data->file_logger)
 		global_data->file_logger->dispose(global_data->file_logger);
@@ -177,9 +183,7 @@ static int initialize_global_data(const config_t *config, global_data_t *global_
 	sigset_t signals;
 
 	memset(global_data, 0, sizeof(*global_data));
-	global_data->config = config;
 	global_data->memory_alignment = get_maximum_cache_line_size();
-	assert(global_data->memory_alignment <= DEFAULT_CACHE_LINE_SIZE);
 	CHECK_ERRNO(sigemptyset, &signals);
 #ifdef NDEBUG
 	CHECK_ERRNO(sigaddset, &signals, SIGINT);
@@ -194,7 +198,7 @@ static int initialize_global_data(const config_t *config, global_data_t *global_
 		goto error;
 
 	if (config->cert && config->key)
-		initialize_openssl(global_data);
+		initialize_openssl(config, global_data);
 
 	const h2o_iovec_t host = h2o_iovec_init(H2O_STRLIT("default"));
 	h2o_hostconf_t * const hostconf = h2o_config_register_host(&global_data->h2o_config,
@@ -220,10 +224,14 @@ static int initialize_global_data(const config_t *config, global_data_t *global_
 			global_data->file_logger = h2o_access_log_register(pathconf, log_handle);
 	}
 
-	global_data->ctx = initialize_thread_contexts(global_data);
+	global_data->global_thread_data = initialize_global_thread_data(config, global_data);
 
-	if (global_data->ctx)
+	if (global_data->global_thread_data) {
+		printf("Number of processors: %zu\nMaximum cache line size: %zu\n",
+		       h2o_numproc(),
+		       global_data->memory_alignment);
 		return EXIT_SUCCESS;
+	}
 
 error:
 	free_global_data(global_data);
@@ -353,10 +361,17 @@ int main(int argc, char *argv[])
 		global_data_t global_data;
 
 		if (initialize_global_data(&config, &global_data) == EXIT_SUCCESS) {
+			thread_context_t ctx;
+
 			setup_process();
-			start_threads(global_data.ctx);
-			connect_to_database(global_data.ctx);
-			event_loop(global_data.ctx);
+			start_threads(global_data.global_thread_data);
+			initialize_thread_context(global_data.global_thread_data, true, &ctx);
+			connect_to_database(&ctx);
+			event_loop(&ctx);
+			// Even though this is global data, we need to close
+			// it before the associated event loop is cleaned up.
+			h2o_socket_close(global_data.signals);
+			free_thread_context(&ctx);
 			free_global_data(&global_data);
 			rc = EXIT_SUCCESS;
 		}

+ 18 - 17
frameworks/C/h2o/src/request_handler.c

@@ -20,6 +20,7 @@
 #include <assert.h>
 #include <h2o.h>
 #include <stdalign.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <string.h>
 #include <yajl/yajl_gen.h>
@@ -48,7 +49,9 @@ static int json_serializer(struct st_h2o_handler_t *self, h2o_req_t *req)
 		CHECK_YAJL_STATUS(yajl_gen_string, gen, YAJL_STRLIT(HELLO_RESPONSE));
 		CHECK_YAJL_STATUS(yajl_gen_map_close, gen);
 
-		if (!send_json_response(gen, NULL, req))
+		// The response is small enough, so that it is simpler to copy it
+		// instead of doing a delayed deallocation of the JSON generator.
+		if (!send_json_response(gen, true, req))
 			return 0;
 
 error_yajl:
@@ -169,31 +172,29 @@ void send_error(http_status_code_t status_code, const char *body, h2o_req_t *req
 	h2o_send_error_generic(req, status_code, status_code_to_string(status_code), body, 0);
 }
 
-int send_json_response(yajl_gen gen, h2o_generator_t *h2o_generator, h2o_req_t *req)
+int send_json_response(yajl_gen gen, bool free_gen, h2o_req_t *req)
 {
 	const unsigned char *buf;
-	h2o_iovec_t h2o_iovec = {.len = 0};
+	size_t len;
 	int ret = EXIT_FAILURE;
 
-	if (yajl_gen_get_buf(gen, &buf, &h2o_iovec.len) == yajl_gen_status_ok) {
-		if (h2o_generator) {
-			h2o_iovec.base = (char *) buf;
-			set_default_response_param(JSON, SIZE_MAX, req);
-			h2o_start_response(req, h2o_generator);
-			h2o_send(req, &h2o_iovec, 1, false);
-			ret = EXIT_SUCCESS;
-		}
-		else {
-			h2o_iovec.base = h2o_mem_alloc_pool(&req->pool, h2o_iovec.len);
+	if (yajl_gen_get_buf(gen, &buf, &len) == yajl_gen_status_ok) {
+		if (free_gen) {
+			char * const body = h2o_mem_alloc_pool(&req->pool, len);
 
-			if (h2o_iovec.base) {
-				memcpy(h2o_iovec.base, buf, h2o_iovec.len);
+			if (body) {
+				memcpy(body, buf, len);
 				yajl_gen_free(gen);
-				set_default_response_param(JSON, h2o_iovec.len, req);
-				h2o_send_inline(req, h2o_iovec.base, h2o_iovec.len);
+				set_default_response_param(JSON, len, req);
+				h2o_send_inline(req, body, len);
 				ret = EXIT_SUCCESS;
 			}
 		}
+		else {
+			set_default_response_param(JSON, len, req);
+			h2o_send_inline(req, (char *) buf, len);
+			ret = EXIT_SUCCESS;
+		}
 	}
 
 	return ret;

+ 2 - 1
frameworks/C/h2o/src/request_handler.h

@@ -22,6 +22,7 @@
 #define REQUEST_H_
 
 #include <h2o.h>
+#include <stdbool.h>
 #include <yajl/yajl_gen.h>
 
 typedef enum {
@@ -44,7 +45,7 @@ const char *get_query_param(const char *query,
                             size_t param_len);
 void register_request_handlers(h2o_hostconf_t *hostconf, h2o_access_log_filehandle_t *log_handle);
 void send_error(http_status_code_t status_code, const char *body, h2o_req_t *req);
-int send_json_response(yajl_gen gen, h2o_generator_t *h2o_generator, h2o_req_t *req);
+int send_json_response(yajl_gen gen, bool free_gen, h2o_req_t *req);
 void send_service_unavailable_error(const char *body, h2o_req_t *req);
 void set_default_response_param(content_type_t content_type,
                                 size_t content_length,

+ 49 - 31
frameworks/C/h2o/src/thread.c

@@ -22,9 +22,12 @@
 #include <errno.h>
 #include <h2o.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdlib.h>
+#include <string.h>
 #include <h2o/serverutil.h>
 #include <sys/epoll.h>
+#include <sys/syscall.h>
 
 #include "database.h"
 #include "error.h"
@@ -35,71 +38,86 @@ static void *run_thread(void *arg);
 
 static void *run_thread(void *arg)
 {
-	connect_to_database(arg);
-	event_loop(arg);
+	thread_context_t ctx;
+
+	initialize_thread_context(arg, false, &ctx);
+	connect_to_database(&ctx);
+	event_loop(&ctx);
+	free_thread_context(&ctx);
 	pthread_exit(NULL);
 }
 
-void free_thread_contexts(global_data_t *global_data)
+void free_thread_context(thread_context_t *ctx)
 {
-	thread_context_t * const ctx = global_data->ctx;
-
-	for (size_t i = 0; i < ctx->global_data->config->thread_num; i++) {
-		if (i)
-			CHECK_ERROR(pthread_join, ctx[i].thread, NULL);
-		else
-			// Even though this is global data, we need to close
-			// it before the associated event loop is cleaned up.
-			h2o_socket_close(global_data->signals);
-
-		free_database_state(ctx[i].event_loop.h2o_ctx.loop, &ctx[i].db_state);
-		free_event_loop(&ctx[i].event_loop);
-	}
-
-	free(ctx);
+	free_database_state(ctx->event_loop.h2o_ctx.loop, &ctx->db_state);
+	free_event_loop(&ctx->event_loop, &ctx->global_thread_data->h2o_receiver);
 }
 
-thread_context_t *initialize_thread_contexts(global_data_t *global_data)
+global_thread_data_t *initialize_global_thread_data(const config_t *config,
+                                                    global_data_t *global_data)
 {
-	const size_t sz = global_data->config->thread_num * sizeof(thread_context_t);
-	thread_context_t * const ret = aligned_alloc(global_data->memory_alignment, sz);
+	const size_t sz = config->thread_num * sizeof(thread_context_t);
+	// The global thread data is modified only at program initialization and termination,
+	// and is not accessed by performance-sensitive code, so false sharing is not a concern.
+	global_thread_data_t * const ret = aligned_alloc(global_data->memory_alignment, sz);
 
 	if (ret) {
 		memset(ret, 0, sz);
 
-		for (size_t i = 0; i < global_data->config->thread_num; i++) {
+		for (size_t i = 0; i < config->thread_num; i++) {
+			ret[i].config = config;
 			ret[i].global_data = global_data;
-			initialize_event_loop(!i, global_data, &ret[i].event_loop);
-			initialize_database_state(ret[i].event_loop.h2o_ctx.loop, &ret[i].db_state);
 		}
 	}
 
 	return ret;
 }
 
-void start_threads(thread_context_t *ctx)
+void initialize_thread_context(global_thread_data_t *global_thread_data,
+                               bool is_main_thread,
+                               thread_context_t *ctx)
+{
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->config = global_thread_data->config;
+	ctx->global_data = global_thread_data->global_data;
+	ctx->global_thread_data = global_thread_data;
+	ctx->tid = syscall(SYS_gettid);
+	ctx->random_seed = ctx->tid;
+	initialize_event_loop(is_main_thread,
+	                      global_thread_data->global_data,
+	                      &global_thread_data->h2o_receiver,
+	                      &ctx->event_loop);
+	initialize_database_state(ctx->event_loop.h2o_ctx.loop, &ctx->db_state);
+	global_thread_data->ctx = ctx;
+}
+
+void start_threads(global_thread_data_t *global_thread_data)
 {
 	const size_t num_cpus = h2o_numproc();
 
 	// The first thread context is used by the main thread.
-	ctx->thread = pthread_self();
+	global_thread_data->thread = pthread_self();
 
-	for (size_t i = 1; i < ctx->global_data->config->thread_num; i++)
-		CHECK_ERROR(pthread_create, &ctx[i].thread, NULL, run_thread, ctx + i);
+	for (size_t i = 1; i < global_thread_data->config->thread_num; i++)
+		CHECK_ERROR(pthread_create,
+		            &global_thread_data[i].thread,
+		            NULL,
+		            run_thread,
+		            global_thread_data + i);
 
 	// If the number of threads is not equal to the number of processors, then let the scheduler
 	// decide how to balance the load.
-	if (ctx->global_data->config->thread_num == num_cpus) {
+	if (global_thread_data->config->thread_num == num_cpus) {
 		const size_t cpusetsize = CPU_ALLOC_SIZE(num_cpus);
 		cpu_set_t * const cpuset = CPU_ALLOC(num_cpus);
 
 		if (!cpuset)
 			abort();
 
-		for (size_t i = 0; i < ctx->global_data->config->thread_num; i++) {
+		for (size_t i = 0; i < global_thread_data->config->thread_num; i++) {
 			CPU_ZERO_S(cpusetsize, cpuset);
 			CPU_SET_S(i, cpusetsize, cpuset);
-			CHECK_ERROR(pthread_setaffinity_np, ctx[i].thread, cpusetsize, cpuset);
+			CHECK_ERROR(pthread_setaffinity_np, global_thread_data[i].thread, cpusetsize, cpuset);
 		}
 
 		CPU_FREE(cpuset);

+ 20 - 12
frameworks/C/h2o/src/thread.h

@@ -24,33 +24,41 @@
 #include <assert.h>
 #include <h2o.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <sys/types.h>
 
 #include "database.h"
 #include "event_loop.h"
 #include "utility.h"
 
-#define DEFAULT_CACHE_LINE_SIZE 64
-
 typedef struct thread_context_t thread_context_t;
 
+typedef struct global_thread_data_t {
+	const config_t *config;
+	thread_context_t *ctx;
+	global_data_t *global_data;
+	h2o_multithread_receiver_t h2o_receiver;
+	pthread_t thread;
+} global_thread_data_t;
+
 struct thread_context_t {
+	const config_t *config;
 	global_data_t *global_data;
+	// global_thread_data contains config and global_data as well,
+	// but keep copies here to avoid some pointer chasing.
+	global_thread_data_t *global_thread_data;
 	unsigned random_seed;
 	pid_t tid;
 	db_state_t db_state;
 	event_loop_t event_loop;
-	pthread_t thread;
-	// Align on the cache line size to prevent false sharing.
-	char padding[49];
 };
 
-static_assert(!(sizeof(thread_context_t) % DEFAULT_CACHE_LINE_SIZE),
-              "The size of the thread_context_t structure must be a "
-              "multiple of the cache line size.");
-
-void free_thread_contexts(global_data_t *global_data);
-thread_context_t *initialize_thread_contexts(global_data_t *global_data);
-void start_threads(thread_context_t *ctx);
+void free_thread_context(thread_context_t *ctx);
+global_thread_data_t *initialize_global_thread_data(const config_t *config,
+                                                    global_data_t *global_data);
+void initialize_thread_context(global_thread_data_t *global_thread_data,
+                               bool is_main_thread,
+                               thread_context_t *ctx);
+void start_threads(global_thread_data_t *global_thread_data);
 
 #endif // THREAD_H_

+ 3 - 3
frameworks/C/h2o/src/tls.c

@@ -136,7 +136,7 @@ void cleanup_openssl(global_data_t *global_data)
 	CHECK_ERROR(pthread_mutexattr_destroy, &openssl_global_data.lock_attr);
 }
 
-void initialize_openssl(global_data_t *global_data)
+void initialize_openssl(const config_t *config, global_data_t *global_data)
 {
 	SSL_library_init();
 	SSL_load_error_strings();
@@ -160,10 +160,10 @@ void initialize_openssl(global_data_t *global_data)
 	global_data->ssl_ctx = SSL_CTX_new(TLSv1_2_server_method());
 	CHECK_OPENSSL_ERROR(SSL_CTX_use_certificate_file,
 	                    global_data->ssl_ctx,
-	                    global_data->config->cert,
+	                    config->cert,
 	                    SSL_FILETYPE_PEM);
 	CHECK_OPENSSL_ERROR(SSL_CTX_use_PrivateKey_file,
 	                    global_data->ssl_ctx,
-	                    global_data->config->key,
+	                    config->key,
 	                    SSL_FILETYPE_PEM);
 }

+ 1 - 1
frameworks/C/h2o/src/tls.h

@@ -24,6 +24,6 @@
 #include "utility.h"
 
 void cleanup_openssl(global_data_t *global_data);
-void initialize_openssl(global_data_t *global_data);
+void initialize_openssl(const config_t *config, global_data_t *global_data);
 
 #endif // TLS_H_

+ 6 - 3
frameworks/C/h2o/src/utility.h

@@ -36,7 +36,7 @@
 #define TOSTRING(x) # x
 #define YAJL_STRLIT(s) (const unsigned char *) (s), sizeof(s) - 1
 
-typedef struct thread_context_t thread_context_t;
+typedef struct global_thread_data_t global_thread_data_t;
 
 typedef struct {
 	const char *bind_address;
@@ -54,12 +54,11 @@ typedef struct {
 } config_t;
 
 typedef struct {
-	const config_t *config;
-	thread_context_t *ctx;
 	h2o_logger_t *file_logger;
 	mustache_template_t *fortunes_template;
 	h2o_socket_t *signals;
 	SSL_CTX *ssl_ctx;
+	global_thread_data_t *global_thread_data;
 	size_t memory_alignment;
 	int listener_sd;
 	int signal_fd;
@@ -67,7 +66,11 @@ typedef struct {
 	h2o_globalconf_t h2o_config;
 } global_data_t;
 
+// Call yajl_gen_free() on the result, even though the JSON generator
+// uses a memory pool; in this way the code remains correct if the
+// underlying memory allocator is changed (e.g. for debugging purposes).
 yajl_gen get_json_generator(h2o_mem_pool_t *pool);
+
 uint32_t get_random_number(uint32_t max_rand, unsigned int *seed);
 
 #endif // UTILITY_H_

+ 72 - 66
frameworks/C/h2o/src/world.c

@@ -53,7 +53,6 @@ typedef struct {
 
 typedef struct {
 	db_query_param_t param;
-	yajl_gen gen;
 	const char *id_pointer;
 	h2o_req_t *req;
 	uint32_t id;
@@ -63,16 +62,18 @@ typedef struct {
 
 typedef struct {
 	single_query_ctx_t single;
+	yajl_gen gen;
 	size_t num_query;
 	size_t num_result;
 	update_state_t update_state;
 	query_result_t res[];
 } multiple_query_ctx_t;
 
+static void cleanup_request(void *data);
 static int do_multiple_queries(update_state_t update_state, h2o_req_t *req);
-static int initialize_single_query_context(h2o_req_t *req,
-                                           on_result_t on_result,
-                                           single_query_ctx_t *query_ctx);
+static void initialize_single_query_context(h2o_req_t *req,
+                                            on_result_t on_result,
+                                            single_query_ctx_t *query_ctx);
 static void on_database_error(db_query_param_t *param, const char *error_string);
 static void on_database_timeout(db_query_param_t *param);
 static result_return_t on_multiple_query_result(db_query_param_t *param, PGresult *result);
@@ -85,6 +86,14 @@ static void serialize_items(const query_result_t *res,
                             yajl_gen gen,
                             h2o_req_t *req);
 
+static void cleanup_request(void *data)
+{
+	const multiple_query_ctx_t * const query_ctx = data;
+
+	if (query_ctx->gen)
+		yajl_gen_free(query_ctx->gen);
+}
+
 static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 {
 	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
@@ -108,16 +117,13 @@ static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 		num_query = MAX_QUERIES;
 
 	const size_t sz = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t);
-	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sz);
+	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc_shared(&req->pool, sz, cleanup_request);
 
-	if (!query_ctx || initialize_single_query_context(req,
-	                                                  on_multiple_query_result,
-	                                                  &query_ctx->single))
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
-	else {
+	if (query_ctx) {
 		// MAX_ID is a relatively small number, so allocate on the stack.
 		DEFINE_BITSET(bitset, MAX_ID);
 
+		initialize_single_query_context(req, on_multiple_query_result, &query_ctx->single);
 		memset(&query_ctx->single + 1,
 		       0,
 		       offsetof(multiple_query_ctx_t, res) - sizeof(query_ctx->single));
@@ -138,43 +144,37 @@ static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 
 		query_ctx->single.id = htonl(query_ctx->res->id);
 
-		if (execute_query(ctx, &query_ctx->single.param)) {
-			yajl_gen_free(query_ctx->single.gen);
+		if (execute_query(ctx, &query_ctx->single.param))
 			send_service_unavailable_error(DB_REQ_ERROR, req);
-		}
+		else
+			// Create a JSON generator while the query is processed.
+			query_ctx->gen = get_json_generator(&req->pool);
 	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 
 	return 0;
 }
 
-static int initialize_single_query_context(h2o_req_t *req,
-                                           on_result_t on_result,
-                                           single_query_ctx_t *query_ctx)
+static void initialize_single_query_context(h2o_req_t *req,
+                                            on_result_t on_result,
+                                            single_query_ctx_t *query_ctx)
 {
-	int ret = EXIT_FAILURE;
-
 	memset(query_ctx, 0, sizeof(*query_ctx));
-	query_ctx->gen = get_json_generator(&req->pool);
-
-	if (query_ctx->gen) {
-		query_ctx->id_format = 1;
-		query_ctx->id_len = sizeof(query_ctx->id);
-		query_ctx->id_pointer = (const char *) &query_ctx->id;
-		query_ctx->param.command = WORLD_TABLE_NAME;
-		query_ctx->param.nParams = 1;
-		query_ctx->param.on_error = on_database_error;
-		query_ctx->param.on_result = on_result;
-		query_ctx->param.on_timeout = on_database_timeout;
-		query_ctx->param.paramFormats = &query_ctx->id_format;
-		query_ctx->param.paramLengths = &query_ctx->id_len;
-		query_ctx->param.paramValues = &query_ctx->id_pointer;
-		query_ctx->param.flags = IS_PREPARED;
-		query_ctx->param.resultFormat = 1;
-		query_ctx->req = req;
-		ret = EXIT_SUCCESS;
-	}
-
-	return ret;
+	query_ctx->id_format = 1;
+	query_ctx->id_len = sizeof(query_ctx->id);
+	query_ctx->id_pointer = (const char *) &query_ctx->id;
+	query_ctx->param.command = WORLD_TABLE_NAME;
+	query_ctx->param.nParams = 1;
+	query_ctx->param.on_error = on_database_error;
+	query_ctx->param.on_result = on_result;
+	query_ctx->param.on_timeout = on_database_timeout;
+	query_ctx->param.paramFormats = &query_ctx->id_format;
+	query_ctx->param.paramLengths = &query_ctx->id_len;
+	query_ctx->param.paramValues = &query_ctx->id_pointer;
+	query_ctx->param.flags = IS_PREPARED;
+	query_ctx->param.resultFormat = 1;
+	query_ctx->req = req;
 }
 
 static void on_database_error(db_query_param_t *param, const char *error_string)
@@ -183,7 +183,6 @@ static void on_database_error(db_query_param_t *param, const char *error_string)
 	                                                              param,
 	                                                              param);
 
-	yajl_gen_free(query_ctx->gen);
 	send_error(BAD_GATEWAY, error_string, query_ctx->req);
 }
 
@@ -193,7 +192,6 @@ static void on_database_timeout(db_query_param_t *param)
 	                                                              param,
 	                                                              param);
 
-	yajl_gen_free(query_ctx->gen);
 	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
 }
 
@@ -228,7 +226,7 @@ static result_return_t on_multiple_query_result(db_query_param_t *param, PGresul
 		else if (query_ctx->update_state == NO_UPDATE) {
 			serialize_items(query_ctx->res,
 			                query_ctx->num_result,
-			                query_ctx->single.gen,
+			                query_ctx->gen,
 			                query_ctx->single.req);
 			return DONE;
 		}
@@ -253,7 +251,6 @@ static result_return_t on_multiple_query_result(db_query_param_t *param, PGresul
 		PQclear(result);
 	}
 
-	yajl_gen_free(query_ctx->single.gen);
 	return DONE;
 }
 
@@ -275,9 +272,17 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		random_number = ntohl(random_number);
 		PQclear(result);
 
-		if (!serialize_item(ntohl(query_ctx->id), random_number, query_ctx->gen) &&
-		    !send_json_response(query_ctx->gen, NULL, query_ctx->req))
-			return DONE;
+		const yajl_gen gen = get_json_generator(&query_ctx->req->pool);
+
+		if (gen) {
+			// The response is small enough, so that it is simpler to copy it
+			// instead of doing a delayed deallocation of the JSON generator.
+			if (!serialize_item(ntohl(query_ctx->id), random_number, gen) &&
+			    !send_json_response(gen, true, query_ctx->req))
+				return DONE;
+
+			yajl_gen_free(gen);
+		}
 
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, query_ctx->req);
 	}
@@ -286,7 +291,6 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		PQclear(result);
 	}
 
-	yajl_gen_free(query_ctx->gen);
 	return DONE;
 }
 
@@ -318,7 +322,7 @@ static result_return_t on_update_result(db_query_param_t *param, PGresult *resul
 
 			serialize_items(query_ctx->res,
 			                query_ctx->num_result,
-			                query_ctx->single.gen,
+			                query_ctx->gen,
 			                query_ctx->single.req);
 			ret = DONE;
 			break;
@@ -329,7 +333,6 @@ static result_return_t on_update_result(db_query_param_t *param, PGresult *resul
 	PQclear(result);
 	return ret;
 error:
-	yajl_gen_free(query_ctx->single.gen);
 	send_error(BAD_GATEWAY, PQresultErrorMessage(result), query_ctx->single.req);
 	PQclear(result);
 	return DONE;
@@ -419,19 +422,25 @@ static void serialize_items(const query_result_t *res,
                             yajl_gen gen,
                             h2o_req_t *req)
 {
-	CHECK_YAJL_STATUS(yajl_gen_array_open, gen);
+	// In principle the JSON generator can be created here, but we do it earlier,
+	// so that it happens in parallel with the database query; we assume that the
+	// allocation will rarely fail, so that the delayed error handling is not
+	// problematic.
+	if (gen) {
+		CHECK_YAJL_STATUS(yajl_gen_array_open, gen);
 
-	for (size_t i = 0; i < num_result; i++)
-		if (serialize_item(res[i].id, res[i].random_number, gen))
-			goto error_yajl;
+		for (size_t i = 0; i < num_result; i++)
+			if (serialize_item(res[i].id, res[i].random_number, gen))
+				goto error_yajl;
 
-	CHECK_YAJL_STATUS(yajl_gen_array_close, gen);
+		CHECK_YAJL_STATUS(yajl_gen_array_close, gen);
 
-	if (send_json_response(gen, NULL, req)) {
-error_yajl:
-		yajl_gen_free(gen);
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+		if (!send_json_response(gen, false, req))
+			return;
 	}
+
+error_yajl:
+	send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 }
 
 int multiple_queries(struct st_h2o_handler_t *self, h2o_req_t *req)
@@ -450,18 +459,15 @@ int single_query(struct st_h2o_handler_t *self, h2o_req_t *req)
 	                                                      req->conn->ctx);
 	single_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*query_ctx));
 
-	if (!query_ctx || initialize_single_query_context(req,
-	                                                  on_single_query_result,
-	                                                  query_ctx))
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
-	else {
+	if (query_ctx) {
+		initialize_single_query_context(req, on_single_query_result, query_ctx);
 		query_ctx->id = htonl(get_random_number(MAX_ID, &ctx->random_seed) + 1);
 
-		if (execute_query(ctx, &query_ctx->param)) {
-			yajl_gen_free(query_ctx->gen);
+		if (execute_query(ctx, &query_ctx->param))
 			send_service_unavailable_error(DB_REQ_ERROR, req);
-		}
 	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 
 	return 0;
 }

+ 1 - 1
toolset/setup/linux/webservers/h2o.sh

@@ -6,7 +6,7 @@ RETCODE=$(fw_exists "${IROOT}/h2o.installed")
   return 0; }
 
 H2O_HOME="${IROOT}/h2o"
-VERSION="2.1.0-beta1"
+VERSION="2.1.0-beta3"
 ARCHIVE="v${VERSION}.tar.gz"
 BUILD_DIR="h2o-${VERSION}"