BUG/MINOR: stats: Make stat_l variable used to dump a stat line thread local

Since ee63d4bd6 ("MEDIUM: stats: integrate static proxies stats in new
stats"), all dumped stats for a given domain, the default ones and the
modules ones, are merged in a signle array to dump them in a generic way.
For this purpose, the stat_l global variable is allocated at startup to
store a line of stats before the dump, i.e. all stats of an entity
(frontend, backend, listener, server or dns nameserver). But this variable
is not thread safe.  If stats are retrieved concurrently by several clients
on different threads, the same variable is used. This leads to corrupted
stats output.

To fix the bug, the stat_l variable is now thread local.

This patch should probably solve issues #972 and #992. It must be backported
to 2.3.

(cherry picked from commit de79cd28ec9e1facc5f2da02cd95dda82ac6fe58)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/stats.c b/src/stats.c
index ad92d71..e64bc53 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -261,7 +261,7 @@
 static size_t stat_count[STATS_DOMAIN_COUNT];
 
 /* one line for stats */
-static struct field *stat_l[STATS_DOMAIN_COUNT];
+static THREAD_LOCAL struct field *stat_l[STATS_DOMAIN_COUNT];
 
 /* list of all registered stats module */
 static struct list stats_module_list[STATS_DOMAIN_COUNT] = {
@@ -4498,12 +4498,7 @@
 		}
 	}
 
-	stat_l[STATS_DOMAIN_PROXY] = malloc(stat_count[STATS_DOMAIN_PROXY] * sizeof(struct field));
-	if (!stat_l[STATS_DOMAIN_PROXY]) {
-		ha_alert("stats: cannot allocate a line for proxy statistics\n");
-		err_code |= ERR_ALERT | ERR_FATAL;
-		return err_code;
-	}
+	/* wait per-thread alloc to perform corresponding stat_l allocation */
 
 	return err_code;
 }
@@ -4536,27 +4531,50 @@
 		return err_code;
 	}
 
-	stat_l[STATS_DOMAIN_DNS] = malloc(stat_count[STATS_DOMAIN_DNS] * sizeof(struct field));
-	if (!stat_l[STATS_DOMAIN_DNS]) {
-		ha_alert("stats: cannot allocate a line for dns statistics\n");
-		err_code |= ERR_ALERT | ERR_FATAL;
-		return err_code;
-	}
+	/* wait per-thread alloc to perform corresponding stat_l allocation */
 
 	return err_code;
 }
 
 REGISTER_CONFIG_POSTPARSER("allocate-stats-dns", allocate_stats_dns_postcheck);
 
-static void deinit_stats(void)
+static int allocate_stat_lines_per_thread(void)
+{
+	int domains[] = { STATS_DOMAIN_PROXY, STATS_DOMAIN_DNS }, i;
+
+	for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
+		const int domain = domains[i];
+
+		stat_l[domain] = malloc(stat_count[domain] * sizeof(struct field));
+		if (!stat_l[domain])
+			return 0;
+	}
+	return 1;
+}
+
+REGISTER_PER_THREAD_ALLOC(allocate_stat_lines_per_thread);
+
+static void deinit_stat_lines_per_thread(void)
 {
 	int domains[] = { STATS_DOMAIN_PROXY, STATS_DOMAIN_DNS }, i;
 
 	for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
 		const int domain = domains[i];
 
-		if (stat_l[domain])
-			free(stat_l[domain]);
+		free(stat_l[domain]);
+		stat_l[domain] = NULL;
+	}
+}
+
+
+REGISTER_PER_THREAD_FREE(deinit_stat_lines_per_thread);
+
+static void deinit_stats(void)
+{
+	int domains[] = { STATS_DOMAIN_PROXY, STATS_DOMAIN_DNS }, i;
+
+	for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
+		const int domain = domains[i];
 
 		if (stat_f[domain])
 			free(stat_f[domain]);