From fd8c59b1a984eeb288c5ecbfad7fe6dda6235140 Mon Sep 17 00:00:00 2001
From: Radek Krejci <rkrejci@cesnet.cz>
Date: Fri, 5 Feb 2016 15:40:20 +0100
Subject: [PATCH] HACK "memory leak" of ly_errno in main thread

It wasn't actually a real memory leak, there was just still reachable
dynamically allocated memory on process exit, because the destructor
registered by pthread_key_create() in the main thread isn't executed
on process termination but only on thread termination (i.e. on
pthread_exit()).

So, on Linux, we are going to use static memory for the main thread's
ly_errno.

Fixes #15
---
 src/common.c | 52 ++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 38 insertions(+), 14 deletions(-)

diff --git a/src/common.c b/src/common.c
index 043aeaf92..979efec2a 100644
--- a/src/common.c
+++ b/src/common.c
@@ -22,10 +22,14 @@
 #define _GNU_SOURCE
 
 #include <assert.h>
+#include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 #include <pthread.h>
+#include <sys/types.h>
+#include <syscall.h>
+#include <unistd.h>
 
 #include "common.h"
 #include "tree_internal.h"
@@ -35,12 +39,42 @@
 LY_ERR ly_errno_int = LY_EINT;
 static pthread_once_t ly_errno_once = PTHREAD_ONCE_INIT;
 static pthread_key_t ly_errno_key;
+#ifdef __linux__
+LY_ERR ly_errno_main = LY_SUCCESS;
+#endif
+
 static void
 ly_errno_createkey(void)
 {
-    if (pthread_key_create(&ly_errno_key, free)) {
+    LY_ERR *value;
+    int r;
+    void (*destr) (void *);
+
+    /* prepare ly_errno storage */
+#ifdef __linux__
+    if (getpid() == syscall(SYS_gettid)) {
+        /* main thread - use global variable instead of thread-specific variable.
+         * This is mainly for valgrind, because in case of the main thread the
+         * destructor registered by pthread_key_create() is not called since
+         * the main thread is terminated rather by return or exit than by
+         * pthread_exit(). */
+        value = &ly_errno_main;
+        destr = NULL;
+    } else {
+#else
+    {
+#endif /* __linux__ */
+        destr = free;
+        value = calloc(1, sizeof *value);
+    }
+
+    /* initiate */
+    while ((r = pthread_key_create(&ly_errno_key, destr)) == EAGAIN);
+    if (r) {
         LOGMEM;
+        return;
     }
+    pthread_setspecific(ly_errno_key, value);
 }
 
 API LY_ERR *
@@ -48,21 +82,11 @@ ly_errno_location(void)
 {
     LY_ERR *retval;
 
-    if (pthread_once(&ly_errno_once, ly_errno_createkey)) {
-        return &ly_errno_int;
-    }
-
+    pthread_once(&ly_errno_once, ly_errno_createkey);
     retval = pthread_getspecific(ly_errno_key);
     if (!retval) {
-        /* first call */
-        retval = calloc(1, sizeof *retval);
-        if (!retval) {
-            return &ly_errno_int;
-        }
-
-        if (pthread_setspecific(ly_errno_key, retval)) {
-            return &ly_errno_int;
-        }
+        /* error */
+        return &ly_errno_int;
     }
 
     return retval;