/*************************************************************************\ * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * SPDX-License-Identifier: EPICS * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /*epicsExit.c*/ /* * Author: Marty Kraimer * Date: 23AUG2004 * Thread exit revisions: Jeff Hill * Date: 06Dec2006 * * Note that epicsExitCallAtThreadExits is currently called directly from the * thread entry wrapper in OS dependent code. That approach might not work * correctly if the thread exits indirectly instead of just returning from * the function specified to epicsThreadCreate. For example the thread might * exit via the exit() call. There might be OS dependent solutions for that * weakness. * */ #include #include #include #include #include "ellLib.h" #include "errlog.h" #include "epicsThread.h" #include "epicsMutex.h" #include "cantProceed.h" #include "epicsExit.h" typedef struct exitNode { ELLNODE node; epicsExitFunc func; void *arg; char name[1]; }exitNode; typedef struct exitPvt { ELLLIST list; } exitPvt; int atExitDebug = 0; static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT; static epicsThreadOnceId exitLaterOnce = EPICS_THREAD_ONCE_INIT; static exitPvt * pExitPvtPerProcess = 0; static epicsMutexId exitPvtLock = 0; static epicsThreadPrivateId exitPvtPerThread = 0; static int exitLaterStatus; static void destroyExitPvt(exitPvt * pep) { ellFree ( &pep->list ); free ( pep ); } static exitPvt * createExitPvt(void) { exitPvt * pep = calloc ( 1, sizeof ( * pep ) ); if ( pep ) { ellInit ( &pep->list ); } return pep; } static void exitPvtOnceFunc(void *pParm) { exitPvtPerThread = epicsThreadPrivateCreate (); assert ( exitPvtPerThread ); exitPvtLock = epicsMutexMustCreate (); } static void epicsExitInit(void) { epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); } static void epicsExitCallAtExitsPvt(exitPvt *pep) { exitNode *pexitNode; while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) { if (atExitDebug && pexitNode->name[0]) fprintf(stderr, "atExit %s(%p)\n", pexitNode->name, pexitNode->arg); else if(atExitDebug) fprintf(stderr, "atExit %p(%p)\n", pexitNode->func, pexitNode->arg); pexitNode->func ( pexitNode->arg ); ellDelete ( & pep->list, & pexitNode->node ); free ( pexitNode ); } } LIBCOM_API void epicsExitCallAtExits(void) { exitPvt * pep = 0; epicsExitInit (); epicsMutexMustLock ( exitPvtLock ); if ( pExitPvtPerProcess ) { pep = pExitPvtPerProcess; pExitPvtPerProcess = 0; } epicsMutexUnlock ( exitPvtLock ); if ( pep ) { epicsExitCallAtExitsPvt ( pep ); destroyExitPvt ( pep ); } } LIBCOM_API void epicsExitCallAtThreadExits(void) { exitPvt * pep; epicsExitInit (); pep = epicsThreadPrivateGet ( exitPvtPerThread ); if ( pep ) { epicsExitCallAtExitsPvt ( pep ); destroyExitPvt ( pep ); epicsThreadPrivateSet ( exitPvtPerThread, 0 ); } } static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const char *name) { int status = -1; exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) ); if ( pExitNode ) { pExitNode->func = func; pExitNode->arg = arg; if(name) strcpy(pExitNode->name, name); ellAdd ( & pep->list, & pExitNode->node ); status = 0; } return status; } LIBCOM_API int epicsAtThreadExit(epicsExitFunc func, void *arg) { exitPvt * pep; epicsExitInit (); pep = epicsThreadPrivateGet ( exitPvtPerThread ); if ( ! pep ) { pep = createExitPvt (); if ( ! pep ) { return -1; } epicsThreadPrivateSet ( exitPvtPerThread, pep ); } return epicsAtExitPvt ( pep, func, arg, NULL ); } LIBCOM_API int epicsAtExit3(epicsExitFunc func, void *arg, const char* name) { int status = -1; epicsExitInit (); epicsMutexMustLock ( exitPvtLock ); if ( !pExitPvtPerProcess ) { pExitPvtPerProcess = createExitPvt (); } if ( pExitPvtPerProcess ) { status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg, name ); } epicsMutexUnlock ( exitPvtLock ); return status; } LIBCOM_API void epicsExit(int status) { epicsExitCallAtExits(); epicsThreadSleep(0.1); exit(status); } static void exitNow(void *junk) { epicsExit(exitLaterStatus); } static void exitLaterOnceFunc(void *raw) { int *status = raw; exitLaterStatus = *status; epicsThreadMustCreate("exitLater", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackSmall), &exitNow, NULL); } LIBCOM_API void epicsExitLater(int status) { epicsThreadOnce(&exitLaterOnce, &exitLaterOnceFunc, &status); } #include "epicsExport.h" epicsExportAddress(int,atExitDebug);