Changeset 1400


Ignore:
Timestamp:
Apr 29, 2004, 5:35:22 AM (21 years ago)
Author:
bird
Message:

Added thread termination callback functionality.

Location:
trunk/src/emx
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified trunk/src/emx/include/InnoTekLIBC/thread.h

    • Property cvs2svn:cvs-rev changed from 1.2 to 1.3
    r1399 r1400  
    141141
    142142
     143/**
     144 * Thread Termination Callback Registration Record. (cool name, right)
     145 * For use with __libc_ThreadRegisterTermCallback().
     146 */
     147typedef struct __libc_ThreadTermCbRegRec
     148{
     149    /** This member must be initialized to NULL. */
     150    struct __libc_ThreadTermCbRegRec   *pNext;
     151    /** Flags field reserved for future use.
     152     * Must be initalized to ZERO.  */
     153    unsigned                            fFlags;
     154    /**
     155     * The callback function.
     156     *
     157     * @param pRegRec   Registration record which the callback was registered with.
     158     * @param fFlags    Reserved for future use. Will always be zero when fFlags
     159     *                  in the RegRec is zero.
     160     */
     161    void                              (*pfnCallback)(struct __libc_ThreadTermCbRegRec *pRegRec, unsigned fFlags);
     162} __LIBC_THREADTERMCBREGREC, *__LIBC_PTHREADTERMCBREGREC;
     163
     164
    143165/*******************************************************************************
    144166*   Global Variables                                                           *
     
    223245
    224246
     247/**
     248 * Register a thread destruction callback.
     249 *
     250 * This will be called when one thread is terminating normally, i.e. calling
     251 * _endthread() or returning from it's thread function.
     252 * When LIBC implements pthreads basics any new non-abnormal thread exit will
     253 * cause a callback too.
     254 *
     255 * @param   pRegRec     Pointer to thread registration record.
     256 *                      This must be initialized as described in the documation of
     257 *                      the structure. After calling this API the memory must not
     258 *                      be touched or freed by the application. It is not possible
     259 *                      to unregister a callback at present.
     260 *
     261 * @remark  We might wanna extend the API at a later point for calling back
     262 *          at abnormal termination and such. Such extensions will be done
     263 *          using the fFlags member of __LIBC_THREADTERMCBREGREC and the fFlags
     264 *          parameter to the callback.
     265 */
     266int     __libc_ThreadRegisterTermCallback(__LIBC_PTHREADTERMCBREGREC pRegRec);
     267
     268/**
     269 * Internal API which is called by a thread exit to work the registered callbacks.
     270 *
     271 * Not called for thread 1.
     272 *
     273 * @param   fFlags  Reserved for termination reasons.
     274 *                  Zero means normal exit, no other codes have been defined.
     275 */
     276void    __libc_threadTermination(unsigned fFlags);
     277
     278
    225279/** @group InnoTek LIBC Thread Local Storage
    226280 * @{
     
    263317int     __libc_TLSSet(int iIndex, void *pvValue);
    264318
     319/**
     320 * Register a thread termination destructor for an TLS entry.
     321 *
     322 * The destructor function will be called when a thread terminates
     323 * in a normal fashion and the TLS entry iTLSIndex of that thread is
     324 * not NULL.
     325 *
     326 * There will be no callbacks in thread 1.
     327 *
     328 * @returns 0 on succces.
     329 * @returns -1 on failure. errno set.
     330 * @param   iTLSIndex       Value returned by __libc_TLSAlloc().
     331 * @param   pfnDestructor   Callback function. Use NULL to unregister a previously
     332 *                          registered destructor.
     333 *
     334 *                          It's pvValue argument is the non-zero value in the
     335 *                          TLS entry for the thread it's called on.
     336 *
     337 *                          It's fFlags argument is reserved for future use, it will
     338 *                          always be zero when the fFlags parameter to this API is zero.
     339 *
     340 * @param   fFlags          Flags reserved for future use. At the moment
     341 *                          only ZERO is allowed.
     342 *
     343 * @remark  The application is not allowed to call __libc_TLSFree() for iIndex when calling
     344 *          this function. The result from doing that is undefined.
     345 */
     346int     __libc_TLSDestructor(int iIndex, void (*pfnDestructor)(void *pvValue, unsigned fFlags), unsigned fFlags);
     347
    265348/** @} */
     349
    266350__END_DECLS
    267351
  • TabularUnified trunk/src/emx/src/lib/libc.def

    • Property cvs2svn:cvs-rev changed from 1.54 to 1.55
    r1399 r1400  
    10951095    "_catopen" @1117
    10961096    "___libc_PathRewrite" @1118
     1097    "___libc_TLSDestructor" @1119
     1098    "___libc_ThreadRegisterTermCallback" @1120
  • TabularUnified trunk/src/emx/src/lib/process/beginthr.c

    • Property cvs2svn:cvs-rev changed from 1.8 to 1.9
    r1399 r1400  
    3333    LIBCLOG_MSG("thread function returned\n");
    3434
    35     FS_SAVE_LOAD();
     35    __libc_threadTermination(0);
    3636    tid = _gettid();
    3737    __libc_threadFree(pThrd);
    3838    __libc_Back_threadEnd(&reg);
     39    FS_SAVE_LOAD();
    3940    LIBCLOG_MSG("calling DosExit(%s, 0)\n", tid == 1 ? "EXIT_PROCESS" : "EXIT_THREAD");
    4041    for (;;)
     
    9899    FS_VAR();
    99100
    100     FS_SAVE_LOAD();
     101    __libc_threadTermination(0);
    101102    tid = _gettid();
    102103    __libc_threadFree(pThrd);
     104    FS_SAVE_LOAD();
    103105    LIBCLOG_MSG("calling DosExit(%s, 0)\n", tid == 1 ? "EXIT_PROCESS" : "EXIT_THREAD");
    104106    for (;;)
  • TabularUnified trunk/src/emx/src/lib/process/thread_internals.c

    • Property cvs2svn:cvs-rev changed from 1.2 to 1.3
    r1399 r1400  
    3030#include "libc-alias.h"
    3131#undef NDEBUG
     32#include <386/builtin.h>
    3233#include <assert.h>
    3334#include <string.h>
     35#include <errno.h>
     36#include <sys/smutex.h>
    3437#include <emx/umalloc.h>
    3538#include <emx/syscalls.h>
     
    4750/** Flags whether or not gPreAllocThrd is used. */
    4851static int              gfPreAllocThrd;
     52
     53/** Head of the thread termination callback list. */
     54static __LIBC_PTHREADTERMCBREGREC   gpTermHead;
     55/** Tail of the thread termination callback list. */
     56static __LIBC_PTHREADTERMCBREGREC   gpTermTail;
     57/** List protection semaphore (spinlock sort). */
     58static _smutex                      gsmtxTerm;
     59
    4960
    5061
     
    109120void  __libc_threadFree(__LIBC_PTHREAD pThrd)
    110121{
    111     LIBCLOG_ENTER("pThrd=%p\n", pThrd);
     122    LIBCLOG_ENTER("pThrd=%p\n", (void *)pThrd);
    112123    /*
    113124     * Clean up members.
     
    125136}
    126137
     138
     139int     __libc_ThreadRegisterTermCallback(__LIBC_PTHREADTERMCBREGREC pRegRec)
     140{
     141    LIBCLOG_ENTER("pRegRec=%p {%p, %u, %p}\n", (void *)pRegRec,
     142                  pRegRec ? (void *)pRegRec->pNext : NULL,
     143                  pRegRec ? pRegRec->fFlags : 0,
     144                  pRegRec ? (void *)pRegRec->pfnCallback : NULL);
     145
     146    /*
     147     * Validate input.
     148     */
     149    if (    pRegRec->pNext
     150        ||  !pRegRec->pfnCallback
     151        ||  pRegRec->fFlags)
     152    {
     153        LIBC_ASSERTM(!pRegRec->pNext, "pNext must be NULL not %p\n", pRegRec->pNext);
     154        LIBC_ASSERTM(pRegRec->pfnCallback, "pfnCallback not be NULL\n");
     155        LIBC_ASSERTM(!pRegRec->fFlags, "fFlags must be ZERO not %u\n", pRegRec->fFlags);
     156        errno = EINVAL;
     157        LIBCLOG_RETURN_INT(-1);
     158    }
     159
     160    /*
     161     * Insert into the LIFO.
     162     */
     163    _smutex_request(&gsmtxTerm);
     164    if (    !pRegRec->pNext
     165        &&  pRegRec != gpTermTail)
     166    {
     167        pRegRec->pNext = gpTermTail;
     168        gpTermTail = pRegRec;
     169        if (!gpTermHead)
     170            gpTermHead = pRegRec;
     171        _smutex_release(&gsmtxTerm);
     172        LIBCLOG_RETURN_INT(0);
     173    }
     174    else
     175    {
     176        _smutex_release(&gsmtxTerm);
     177        LIBC_ASSERTM_FAILED("Double registration of %p\n", pRegRec);
     178        LIBCLOG_RETURN_INT(-1);
     179    }
     180}
     181
     182
     183void    __libc_threadTermination(unsigned fFlags)
     184{
     185    LIBCLOG_ENTER("fFlags=%u\n", fFlags);
     186    __LIBC_PTHREADTERMCBREGREC  pCur;
     187    __LIBC_PTHREADTERMCBREGREC  pTail;
     188
     189    /*
     190     * We'll need to do this in a safe manner.
     191     *
     192     * ASSUME no removal of records.
     193     * Thus we just pick the head and tail record and walk them.
     194     */
     195    _smutex_request(&gsmtxTerm);
     196    pTail = gpTermTail;
     197    pCur = gpTermHead;
     198    _smutex_release(&gsmtxTerm);
     199
     200    while (pCur)
     201    {
     202        /* call it */
     203        LIBCLOG_MSG("calling %p with %p\n", pCur->pfnCallback, pCur);
     204        pCur->pfnCallback(pCur, fFlags);
     205
     206        /* next */
     207        if (pCur == pTail)
     208            break;
     209        pCur = pCur->pNext;
     210    }
     211
     212    LIBCLOG_RETURN_VOID();
     213}
  • TabularUnified trunk/src/emx/src/lib/process/tls.c

    • Property cvs2svn:cvs-rev changed from 1.2 to 1.3
    r1399 r1400  
    3131#include <errno.h>
    3232#include <sys/limits.h>
     33#include <sys/smutex.h>
    3334#include <InnoTekLIBC/thread.h>
    3435#define __LIBC_LOG_GROUP __LIBC_LOG_GRP_THREAD
     
    5051#endif
    5152
     53/*******************************************************************************
     54*   Internal Functions                                                         *
     55*******************************************************************************/
     56static void __libc_tlsThreadDestructor(__LIBC_PTHREADTERMCBREGREC pRegRec, unsigned fFlags);
     57
    5258
    5359/*******************************************************************************
    5460*   Global Variables                                                           *
    5561*******************************************************************************/
     62/** Number of allocated TLS items.
     63 * Updated atomically. Update before allocation and after free. */
     64static unsigned gcTLSAllocated;
     65
    5666/** TLS allocation bitmap map.
    5767 * Set means allocated, clear means free. Updated atomically. */
    58 static unsigned auBitmap[(__LIBC_TLS_MAX + ENTRY_BITS - 1) / ENTRY_BITS];
    59 
    60 /** Number of allocated TLS items.
    61  * Updated atomically. Update before allocation and after free. */
    62 static unsigned cTLSAllocated;
     68static unsigned gauBitmap[(__LIBC_TLS_MAX + ENTRY_BITS - 1) / ENTRY_BITS];
     69
     70/** TLS Per Thread Destructors. */
     71static   void (*gapfnDestructors[__LIBC_TLS_MAX])(void *, unsigned);
     72
     73/** Thread Termination Registration Record. */
     74static __LIBC_THREADTERMCBREGREC    gThrdTermRegRec =
     75{
     76    NULL, 0, __libc_tlsThreadDestructor
     77};
    6378
    6479
     
    7186     * Space left?
    7287     */
    73     if (__atomic_increment_max(&cTLSAllocated, __LIBC_TLS_MAX))
    74     {
    75         LIBC_ASSERTM_FAILED("Out of TLS entries! cur=%d max=%d\n", cTLSAllocated, __LIBC_TLS_MAX);
     88    if (__atomic_increment_max(&gcTLSAllocated, __LIBC_TLS_MAX))
     89    {
     90        LIBC_ASSERTM_FAILED("Out of TLS entries! cur=%d max=%d\n", gcTLSAllocated, __LIBC_TLS_MAX);
    7691        LIBCLOG_RETURN_INT(-1);
    7792    }
     
    86101         * Scan the bitmap int by int.
    87102         */
    88         unsigned *pu = &auBitmap[0];
    89         while (pu < &auBitmap[sizeof(auBitmap) / sizeof(auBitmap[0])])
     103        unsigned *pu = &gauBitmap[0];
     104        while (pu < &gauBitmap[sizeof(gauBitmap) / sizeof(gauBitmap[0])])
    90105        {
    91106            if (*pu != ~0)
     
    101116                        if (!__atomic_set_bit(pu, uBit))
    102117                        {
    103                             int iBitRet = (pu - &auBitmap[0]) * ENTRY_BITS + uBit;
     118                            int iBitRet = (pu - &gauBitmap[0]) * ENTRY_BITS + uBit;
    104119                            if (iBitRet < __LIBC_TLS_MAX)
     120                            {
     121                                gapfnDestructors[iIndex] = NULL;
    105122                                LIBCLOG_RETURN_INT(iBitRet);
     123                            }
    106124                        }
    107125                    }
     
    114132    }
    115133
    116     LIBC_ASSERTM_FAILED("We're giving up finding a free enter!!! cur=%d max=%d\n", cTLSAllocated, __LIBC_TLS_MAX);
     134    LIBC_ASSERTM_FAILED("We're giving up finding a free enter!!! cur=%d max=%d\n", gcTLSAllocated, __LIBC_TLS_MAX);
    117135    LIBCLOG_RETURN_INT(-1);
    118136}
     
    127145    if (    iIndex < 0
    128146        ||  iIndex >= __LIBC_TLS_MAX
    129         ||  !__atomic_test_bit(&auBitmap[0], iIndex)
     147        ||  !__atomic_test_bit(&gauBitmap[0], iIndex)
    130148            )
    131149    {
     
    138156     * Clear the bitmap bit.
    139157     */
    140     __atomic_clear_bit(&auBitmap[0], iIndex);
     158    gapfnDestructors[iIndex] = NULL;
     159    __atomic_clear_bit(&gauBitmap[0], iIndex);
    141160
    142161    /*
    143162     * Decrement the allocation count.
    144163     */
    145     LIBC_ASSERT(cTLSAllocated > 0);
    146     __atomic_decrement(&cTLSAllocated);
     164    LIBC_ASSERT(gcTLSAllocated > 0);
     165    __atomic_decrement(&gcTLSAllocated);
    147166    LIBCLOG_RETURN_INT(0);
    148167}
     
    157176    if (    iIndex < 0
    158177        ||  iIndex >= __LIBC_TLS_MAX
    159         ||  !__atomic_test_bit(&auBitmap[0], iIndex)
     178        ||  !__atomic_test_bit(&gauBitmap[0], iIndex)
    160179            )
    161180    {
     
    180199    if (    iIndex < 0
    181200        ||  iIndex >= __LIBC_TLS_MAX
    182         ||  !__atomic_test_bit(&auBitmap[0], iIndex)
     201        ||  !__atomic_test_bit(&gauBitmap[0], iIndex)
    183202            )
    184203    {
     
    195214}
    196215
     216
     217
     218int     __libc_TLSDestructor(int iIndex, void (*pfnDestructor)(void *pvValue, unsigned fFlags), unsigned fFlags)
     219{
     220    LIBCLOG_ENTER("iIndex=%d pfnDestructor=%p\n", iIndex, pfnDestructor);
     221    static _smutex      smtxInit;
     222    static volatile int fInited;
     223
     224    /*
     225     * Validate index
     226     */
     227    if (    iIndex < 0
     228        ||  iIndex >= __LIBC_TLS_MAX
     229        ||  !__atomic_test_bit(&gauBitmap[0], iIndex)
     230            )
     231    {
     232        LIBC_ASSERTM_FAILED("Bad index %d. (max=%d)\n", iIndex, __LIBC_TLS_MAX);
     233        errno = EINVAL;
     234        LIBCLOG_RETURN_INT(-1);
     235    }
     236    if (fFlags)
     237    {
     238        LIBC_ASSERTM_FAILED("fFlags must be zero not %x!\n", fFlags);
     239        errno = EINVAL;
     240        LIBCLOG_RETURN_INT(-1);
     241    }
     242
     243    /*
     244     * Init the destructor if needed.
     245     */
     246    if (!fInited)
     247    {
     248        _smutex_request(&smtxInit);
     249        if (!fInited)
     250        {
     251            __libc_ThreadRegisterTermCallback(&gThrdTermRegRec);
     252            fInited = 1;
     253        }
     254        _smutex_release(&smtxInit);
     255    }
     256
     257    /*
     258     * Update destructor index.
     259     */
     260    LIBCLOG_MSG("old=%p new=%p\n", gapfnDestructors[iIndex], pfnDestructor);
     261    gapfnDestructors[iIndex] = pfnDestructor;
     262    LIBCLOG_RETURN_INT(0);
     263}
     264
     265
     266/**
     267 * Callback function for thread termination.
     268 * See __LIBC_THREADTERMCBREGREC::pfnCallback for parameter details.
     269 *
     270 * This is registered by the first call to __libc_TLSDestructor() and will
     271 * be called back on normal thread terminations. It's function is to iterate
     272 * the registered TLS destructors and calling back the registered TLS
     273 * destructors so they can perform proper per thread cleanups.
     274 */
     275static void __libc_tlsThreadDestructor(__LIBC_PTHREADTERMCBREGREC pRegRec, unsigned fFlags)
     276{
     277    LIBCLOG_ENTER("pRegRec=%p, fFlags=%d\n", (void *)pRegRec, fFlags);
     278
     279    /* get thread structure. */
     280    __LIBC_PTHREAD  pThrd = __libc_threadCurrentNoAuto();
     281    if (!pThrd)
     282        LIBCLOG_RETURN_MSG_VOID("ret void (thread struct not initialized)\n");
     283
     284
     285    /* iterate it word by word. */
     286    int i;
     287    unsigned *pu = (unsigned *)&gauBitmap[0];
     288    for (i = 0; i < sizeof(gauBitmap) / sizeof(unsigned); i++, pu++)
     289    {
     290         if (*pu)
     291         {
     292             /* iterate the word byte by byte. */
     293             int j;
     294             unsigned char *puch = (unsigned char *)&gauBitmap[0];
     295             for (j = 0; j < sizeof(unsigned char); j++, puch++)
     296             {
     297                if (*puch)
     298                {
     299                    /* iterate byte entry by entry. */
     300                    int k = i * sizeof(unsigned) * 8 + j * 8;   
     301                    int kEnd = k + 8;   
     302                    while (k < kEnd)
     303                    {
     304                        void (*pfnDestructor)(void *, unsigned);
     305                        if (    __atomic_test_bit(&gauBitmap[0], k)   
     306                            &&  k < __LIBC_TLS_MAX
     307                            &&  pThrd->apvTLS[k]
     308                            &&  (pfnDestructor = gapfnDestructors[k]) != NULL)
     309                        {
     310                            LIBCLOG_MSG("tls %d: calling %p with %p\n", k, (void *)pfnDestructor, pThrd->apvTLS[k]);
     311                            pfnDestructor(pThrd->apvTLS[k], fFlags);
     312                        }
     313
     314                        /* next */
     315                        k++;
     316                    }
     317                }
     318             }
     319         }
     320    }
     321    LIBCLOG_RETURN_VOID();
     322}
Note: See TracChangeset for help on using the changeset viewer.