Index: trunk/src/emx/include/InnoTekLIBC/thread.h
===================================================================
--- trunk/src/emx/include/InnoTekLIBC/thread.h	(revision 1399)
+++ trunk/src/emx/include/InnoTekLIBC/thread.h	(revision 1400)
@@ -141,4 +141,26 @@
 
 
+/**
+ * Thread Termination Callback Registration Record. (cool name, right)
+ * For use with __libc_ThreadRegisterTermCallback().
+ */
+typedef struct __libc_ThreadTermCbRegRec
+{
+    /** This member must be initialized to NULL. */
+    struct __libc_ThreadTermCbRegRec   *pNext;
+    /** Flags field reserved for future use. 
+     * Must be initalized to ZERO.  */
+    unsigned                            fFlags;
+    /** 
+     * The callback function. 
+     *
+     * @param pRegRec   Registration record which the callback was registered with.
+     * @param fFlags    Reserved for future use. Will always be zero when fFlags 
+     *                  in the RegRec is zero.
+     */
+    void                              (*pfnCallback)(struct __libc_ThreadTermCbRegRec *pRegRec, unsigned fFlags);
+} __LIBC_THREADTERMCBREGREC, *__LIBC_PTHREADTERMCBREGREC;
+
+
 /*******************************************************************************
 *   Global Variables                                                           *
@@ -223,4 +245,36 @@
 
 
+/** 
+ * Register a thread destruction callback.
+ *
+ * This will be called when one thread is terminating normally, i.e. calling 
+ * _endthread() or returning from it's thread function.
+ * When LIBC implements pthreads basics any new non-abnormal thread exit will 
+ * cause a callback too.
+ *
+ * @param   pRegRec     Pointer to thread registration record.
+ *                      This must be initialized as described in the documation of
+ *                      the structure. After calling this API the memory must not
+ *                      be touched or freed by the application. It is not possible
+ *                      to unregister a callback at present.
+ *
+ * @remark  We might wanna extend the API at a later point for calling back 
+ *          at abnormal termination and such. Such extensions will be done 
+ *          using the fFlags member of __LIBC_THREADTERMCBREGREC and the fFlags
+ *          parameter to the callback.
+ */
+int     __libc_ThreadRegisterTermCallback(__LIBC_PTHREADTERMCBREGREC pRegRec);
+
+/**
+ * Internal API which is called by a thread exit to work the registered callbacks.
+ *
+ * Not called for thread 1.
+ * 
+ * @param   fFlags  Reserved for termination reasons.
+ *                  Zero means normal exit, no other codes have been defined.
+ */
+void    __libc_threadTermination(unsigned fFlags);
+
+
 /** @group InnoTek LIBC Thread Local Storage
  * @{
@@ -263,5 +317,35 @@
 int     __libc_TLSSet(int iIndex, void *pvValue);
 
+/** 
+ * Register a thread termination destructor for an TLS entry.
+ *
+ * The destructor function will be called when a thread terminates
+ * in a normal fashion and the TLS entry iTLSIndex of that thread is
+ * not NULL. 
+ *
+ * There will be no callbacks in thread 1.
+ *
+ * @returns 0 on succces.
+ * @returns -1 on failure. errno set.
+ * @param   iTLSIndex       Value returned by __libc_TLSAlloc().
+ * @param   pfnDestructor   Callback function. Use NULL to unregister a previously
+ *                          registered destructor.
+ *
+ *                          It's pvValue argument is the non-zero value in the
+ *                          TLS entry for the thread it's called on.
+ *
+ *                          It's fFlags argument is reserved for future use, it will
+ *                          always be zero when the fFlags parameter to this API is zero.
+ *
+ * @param   fFlags          Flags reserved for future use. At the moment
+ *                          only ZERO is allowed.
+ *
+ * @remark  The application is not allowed to call __libc_TLSFree() for iIndex when calling
+ *          this function. The result from doing that is undefined.
+ */
+int     __libc_TLSDestructor(int iIndex, void (*pfnDestructor)(void *pvValue, unsigned fFlags), unsigned fFlags);
+
 /** @} */
+
 __END_DECLS
 
Index: trunk/src/emx/src/lib/libc.def
===================================================================
--- trunk/src/emx/src/lib/libc.def	(revision 1399)
+++ trunk/src/emx/src/lib/libc.def	(revision 1400)
@@ -1095,2 +1095,4 @@
     "_catopen" @1117
     "___libc_PathRewrite" @1118
+    "___libc_TLSDestructor" @1119
+    "___libc_ThreadRegisterTermCallback" @1120
Index: trunk/src/emx/src/lib/process/beginthr.c
===================================================================
--- trunk/src/emx/src/lib/process/beginthr.c	(revision 1399)
+++ trunk/src/emx/src/lib/process/beginthr.c	(revision 1400)
@@ -33,8 +33,9 @@
     LIBCLOG_MSG("thread function returned\n");
 
-    FS_SAVE_LOAD();
+    __libc_threadTermination(0);
     tid = _gettid();
     __libc_threadFree(pThrd);
     __libc_Back_threadEnd(&reg);
+    FS_SAVE_LOAD();
     LIBCLOG_MSG("calling DosExit(%s, 0)\n", tid == 1 ? "EXIT_PROCESS" : "EXIT_THREAD");
     for (;;)
@@ -98,7 +99,8 @@
     FS_VAR();
 
-    FS_SAVE_LOAD();
+    __libc_threadTermination(0);
     tid = _gettid();
     __libc_threadFree(pThrd);
+    FS_SAVE_LOAD();
     LIBCLOG_MSG("calling DosExit(%s, 0)\n", tid == 1 ? "EXIT_PROCESS" : "EXIT_THREAD");
     for (;;)
Index: trunk/src/emx/src/lib/process/thread_internals.c
===================================================================
--- trunk/src/emx/src/lib/process/thread_internals.c	(revision 1399)
+++ trunk/src/emx/src/lib/process/thread_internals.c	(revision 1400)
@@ -30,6 +30,9 @@
 #include "libc-alias.h"
 #undef NDEBUG
+#include <386/builtin.h>
 #include <assert.h>
 #include <string.h>
+#include <errno.h>
+#include <sys/smutex.h>
 #include <emx/umalloc.h>
 #include <emx/syscalls.h>
@@ -47,4 +50,12 @@
 /** Flags whether or not gPreAllocThrd is used. */
 static int              gfPreAllocThrd;
+
+/** Head of the thread termination callback list. */
+static __LIBC_PTHREADTERMCBREGREC   gpTermHead;
+/** Tail of the thread termination callback list. */
+static __LIBC_PTHREADTERMCBREGREC   gpTermTail;
+/** List protection semaphore (spinlock sort). */
+static _smutex                      gsmtxTerm;
+
 
 
@@ -109,5 +120,5 @@
 void  __libc_threadFree(__LIBC_PTHREAD pThrd)
 {
-    LIBCLOG_ENTER("pThrd=%p\n", pThrd);
+    LIBCLOG_ENTER("pThrd=%p\n", (void *)pThrd);
     /*
      * Clean up members.
@@ -125,2 +136,78 @@
 }
 
+
+int     __libc_ThreadRegisterTermCallback(__LIBC_PTHREADTERMCBREGREC pRegRec)
+{
+    LIBCLOG_ENTER("pRegRec=%p {%p, %u, %p}\n", (void *)pRegRec, 
+                  pRegRec ? (void *)pRegRec->pNext : NULL,
+                  pRegRec ? pRegRec->fFlags : 0, 
+                  pRegRec ? (void *)pRegRec->pfnCallback : NULL);
+
+    /*
+     * Validate input.
+     */
+    if (    pRegRec->pNext 
+        ||  !pRegRec->pfnCallback
+        ||  pRegRec->fFlags)
+    {
+        LIBC_ASSERTM(!pRegRec->pNext, "pNext must be NULL not %p\n", pRegRec->pNext);
+        LIBC_ASSERTM(pRegRec->pfnCallback, "pfnCallback not be NULL\n");
+        LIBC_ASSERTM(!pRegRec->fFlags, "fFlags must be ZERO not %u\n", pRegRec->fFlags);
+        errno = EINVAL;
+        LIBCLOG_RETURN_INT(-1);
+    }
+
+    /*
+     * Insert into the LIFO.
+     */
+    _smutex_request(&gsmtxTerm);
+    if (    !pRegRec->pNext 
+        &&  pRegRec != gpTermTail)
+    {
+        pRegRec->pNext = gpTermTail;
+        gpTermTail = pRegRec;
+        if (!gpTermHead)
+            gpTermHead = pRegRec;
+        _smutex_release(&gsmtxTerm);
+        LIBCLOG_RETURN_INT(0);
+    }
+    else
+    {
+        _smutex_release(&gsmtxTerm);
+        LIBC_ASSERTM_FAILED("Double registration of %p\n", pRegRec);
+        LIBCLOG_RETURN_INT(-1);
+    }
+}
+
+
+void    __libc_threadTermination(unsigned fFlags)
+{
+    LIBCLOG_ENTER("fFlags=%u\n", fFlags);
+    __LIBC_PTHREADTERMCBREGREC  pCur;
+    __LIBC_PTHREADTERMCBREGREC  pTail;
+
+    /*
+     * We'll need to do this in a safe manner.
+     *
+     * ASSUME no removal of records.
+     * Thus we just pick the head and tail record and walk them.
+     */
+    _smutex_request(&gsmtxTerm);
+    pTail = gpTermTail;
+    pCur = gpTermHead;
+    _smutex_release(&gsmtxTerm);
+
+    while (pCur)
+    {
+        /* call it */
+        LIBCLOG_MSG("calling %p with %p\n", pCur->pfnCallback, pCur);
+        pCur->pfnCallback(pCur, fFlags);
+
+        /* next */
+        if (pCur == pTail)
+            break;
+        pCur = pCur->pNext;
+    }
+
+    LIBCLOG_RETURN_VOID();
+}
Index: trunk/src/emx/src/lib/process/tls.c
===================================================================
--- trunk/src/emx/src/lib/process/tls.c	(revision 1399)
+++ trunk/src/emx/src/lib/process/tls.c	(revision 1400)
@@ -31,4 +31,5 @@
 #include <errno.h>
 #include <sys/limits.h>
+#include <sys/smutex.h>
 #include <InnoTekLIBC/thread.h>
 #define __LIBC_LOG_GROUP __LIBC_LOG_GRP_THREAD
@@ -50,15 +51,29 @@
 #endif
 
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+static void __libc_tlsThreadDestructor(__LIBC_PTHREADTERMCBREGREC pRegRec, unsigned fFlags);
+
 
 /*******************************************************************************
 *   Global Variables                                                           *
 *******************************************************************************/
+/** Number of allocated TLS items.
+ * Updated atomically. Update before allocation and after free. */
+static unsigned gcTLSAllocated;
+
 /** TLS allocation bitmap map.
  * Set means allocated, clear means free. Updated atomically. */
-static unsigned auBitmap[(__LIBC_TLS_MAX + ENTRY_BITS - 1) / ENTRY_BITS];
-
-/** Number of allocated TLS items.
- * Updated atomically. Update before allocation and after free. */
-static unsigned cTLSAllocated;
+static unsigned gauBitmap[(__LIBC_TLS_MAX + ENTRY_BITS - 1) / ENTRY_BITS];
+
+/** TLS Per Thread Destructors. */
+static   void (*gapfnDestructors[__LIBC_TLS_MAX])(void *, unsigned);
+
+/** Thread Termination Registration Record. */
+static __LIBC_THREADTERMCBREGREC    gThrdTermRegRec =
+{
+    NULL, 0, __libc_tlsThreadDestructor
+};
 
 
@@ -71,7 +86,7 @@
      * Space left?
      */
-    if (__atomic_increment_max(&cTLSAllocated, __LIBC_TLS_MAX))
-    {
-        LIBC_ASSERTM_FAILED("Out of TLS entries! cur=%d max=%d\n", cTLSAllocated, __LIBC_TLS_MAX);
+    if (__atomic_increment_max(&gcTLSAllocated, __LIBC_TLS_MAX))
+    {
+        LIBC_ASSERTM_FAILED("Out of TLS entries! cur=%d max=%d\n", gcTLSAllocated, __LIBC_TLS_MAX);
         LIBCLOG_RETURN_INT(-1);
     }
@@ -86,6 +101,6 @@
          * Scan the bitmap int by int.
          */
-        unsigned *pu = &auBitmap[0];
-        while (pu < &auBitmap[sizeof(auBitmap) / sizeof(auBitmap[0])])
+        unsigned *pu = &gauBitmap[0];
+        while (pu < &gauBitmap[sizeof(gauBitmap) / sizeof(gauBitmap[0])])
         {
             if (*pu != ~0)
@@ -101,7 +116,10 @@
                         if (!__atomic_set_bit(pu, uBit))
                         {
-                            int iBitRet = (pu - &auBitmap[0]) * ENTRY_BITS + uBit;
+                            int iBitRet = (pu - &gauBitmap[0]) * ENTRY_BITS + uBit;
                             if (iBitRet < __LIBC_TLS_MAX)
+                            {
+                                gapfnDestructors[iIndex] = NULL;
                                 LIBCLOG_RETURN_INT(iBitRet);
+                            }
                         }
                     }
@@ -114,5 +132,5 @@
     }
 
-    LIBC_ASSERTM_FAILED("We're giving up finding a free enter!!! cur=%d max=%d\n", cTLSAllocated, __LIBC_TLS_MAX);
+    LIBC_ASSERTM_FAILED("We're giving up finding a free enter!!! cur=%d max=%d\n", gcTLSAllocated, __LIBC_TLS_MAX);
     LIBCLOG_RETURN_INT(-1);
 }
@@ -127,5 +145,5 @@
     if (    iIndex < 0
         ||  iIndex >= __LIBC_TLS_MAX
-        ||  !__atomic_test_bit(&auBitmap[0], iIndex)
+        ||  !__atomic_test_bit(&gauBitmap[0], iIndex)
             )
     {
@@ -138,11 +156,12 @@
      * Clear the bitmap bit.
      */
-    __atomic_clear_bit(&auBitmap[0], iIndex);
+    gapfnDestructors[iIndex] = NULL;
+    __atomic_clear_bit(&gauBitmap[0], iIndex);
 
     /*
      * Decrement the allocation count.
      */
-    LIBC_ASSERT(cTLSAllocated > 0);
-    __atomic_decrement(&cTLSAllocated);
+    LIBC_ASSERT(gcTLSAllocated > 0);
+    __atomic_decrement(&gcTLSAllocated);
     LIBCLOG_RETURN_INT(0);
 }
@@ -157,5 +176,5 @@
     if (    iIndex < 0
         ||  iIndex >= __LIBC_TLS_MAX
-        ||  !__atomic_test_bit(&auBitmap[0], iIndex)
+        ||  !__atomic_test_bit(&gauBitmap[0], iIndex)
             )
     {
@@ -180,5 +199,5 @@
     if (    iIndex < 0
         ||  iIndex >= __LIBC_TLS_MAX
-        ||  !__atomic_test_bit(&auBitmap[0], iIndex)
+        ||  !__atomic_test_bit(&gauBitmap[0], iIndex)
             )
     {
@@ -195,2 +214,109 @@
 }
 
+
+
+int     __libc_TLSDestructor(int iIndex, void (*pfnDestructor)(void *pvValue, unsigned fFlags), unsigned fFlags)
+{
+    LIBCLOG_ENTER("iIndex=%d pfnDestructor=%p\n", iIndex, pfnDestructor);
+    static _smutex      smtxInit;
+    static volatile int fInited;
+
+    /*
+     * Validate index
+     */
+    if (    iIndex < 0
+        ||  iIndex >= __LIBC_TLS_MAX
+        ||  !__atomic_test_bit(&gauBitmap[0], iIndex)
+            )
+    {
+        LIBC_ASSERTM_FAILED("Bad index %d. (max=%d)\n", iIndex, __LIBC_TLS_MAX);
+        errno = EINVAL;
+        LIBCLOG_RETURN_INT(-1);
+    }
+    if (fFlags)
+    {
+        LIBC_ASSERTM_FAILED("fFlags must be zero not %x!\n", fFlags);
+        errno = EINVAL;
+        LIBCLOG_RETURN_INT(-1);
+    }
+
+    /*
+     * Init the destructor if needed.
+     */
+    if (!fInited)
+    {
+        _smutex_request(&smtxInit);
+        if (!fInited)
+        {
+            __libc_ThreadRegisterTermCallback(&gThrdTermRegRec);
+            fInited = 1;
+        }
+        _smutex_release(&smtxInit);
+    }
+
+    /*
+     * Update destructor index.
+     */
+    LIBCLOG_MSG("old=%p new=%p\n", gapfnDestructors[iIndex], pfnDestructor);
+    gapfnDestructors[iIndex] = pfnDestructor;
+    LIBCLOG_RETURN_INT(0);
+}
+
+
+/**
+ * Callback function for thread termination.
+ * See __LIBC_THREADTERMCBREGREC::pfnCallback for parameter details.
+ *
+ * This is registered by the first call to __libc_TLSDestructor() and will
+ * be called back on normal thread terminations. It's function is to iterate 
+ * the registered TLS destructors and calling back the registered TLS 
+ * destructors so they can perform proper per thread cleanups.
+ */
+static void __libc_tlsThreadDestructor(__LIBC_PTHREADTERMCBREGREC pRegRec, unsigned fFlags)
+{
+    LIBCLOG_ENTER("pRegRec=%p, fFlags=%d\n", (void *)pRegRec, fFlags);
+
+    /* get thread structure. */
+    __LIBC_PTHREAD  pThrd = __libc_threadCurrentNoAuto();
+    if (!pThrd)
+        LIBCLOG_RETURN_MSG_VOID("ret void (thread struct not initialized)\n");
+
+
+    /* iterate it word by word. */
+    int i;
+    unsigned *pu = (unsigned *)&gauBitmap[0];
+    for (i = 0; i < sizeof(gauBitmap) / sizeof(unsigned); i++, pu++)
+    {
+         if (*pu)
+         {
+             /* iterate the word byte by byte. */
+             int j;
+             unsigned char *puch = (unsigned char *)&gauBitmap[0];
+             for (j = 0; j < sizeof(unsigned char); j++, puch++)
+             {
+                if (*puch)
+                {
+                    /* iterate byte entry by entry. */
+                    int k = i * sizeof(unsigned) * 8 + j * 8;   
+                    int kEnd = k + 8;   
+                    while (k < kEnd)
+                    {
+                        void (*pfnDestructor)(void *, unsigned);
+                        if (    __atomic_test_bit(&gauBitmap[0], k)    
+                            &&  k < __LIBC_TLS_MAX
+                            &&  pThrd->apvTLS[k]
+                            &&  (pfnDestructor = gapfnDestructors[k]) != NULL)
+                        {
+                            LIBCLOG_MSG("tls %d: calling %p with %p\n", k, (void *)pfnDestructor, pThrd->apvTLS[k]);
+                            pfnDestructor(pThrd->apvTLS[k], fFlags);
+                        }
+
+                        /* next */
+                        k++;
+                    }
+                }
+             }
+         }
+    }
+    LIBCLOG_RETURN_VOID();
+}
