787 lines
24 KiB
C
787 lines
24 KiB
C
/***
|
|
*crt0dat.c - 32-bit C run-time initialization/termination routines
|
|
*
|
|
* Copyright (c) 1986-2001, Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose:
|
|
* This module contains the routines _cinit, exit, and _exit
|
|
* for C run-time startup and termination. _cinit and exit
|
|
* are called from the _astart code in crt0.asm.
|
|
* This module also defines several data variables used by the
|
|
* runtime.
|
|
*
|
|
* [NOTE: Lock segment definitions are at end of module.]
|
|
*
|
|
*Revision History:
|
|
* 06-28-89 PHG Module created, based on asm version
|
|
* 04-09-90 GJF Added #include <cruntime.h>. Made calling type
|
|
* explicit (_CALLTYPE1 or _CALLTYPE4). Also, fixed
|
|
* the copyright.
|
|
* 04-10-90 GJF Fixed compiler warnings (-W3).
|
|
* 05-21-90 GJF Added #undef _NFILE_ (temporary hack) and fixed the
|
|
* indents.
|
|
* 08-31-90 GJF Removed 32 from API names.
|
|
* 09-25-90 GJF Merged tree version with local (8-31 and 5-21 changes).
|
|
* 10-08-90 GJF New-style function declarators.
|
|
* 10-12-90 GJF Removed divide by 0 stuff.
|
|
* 10-18-90 GJF Added _pipech[] array.
|
|
* 11-05-90 GJF Added _umaskval.
|
|
* 12-04-90 GJF Added _osfinfo[] definition for Win32 target. Note that
|
|
* the Win32 support is still incomplete!
|
|
* 12-04-90 SRW Changed to include <oscalls.h> instead of <doscalls.h>
|
|
* 12-04-90 SRW Added _osfile back for win32. Changed _osfinfo from
|
|
* an array of structures to an array of 32-bit handles
|
|
* (_osfhnd)
|
|
* 12-28-90 SRW Added _CRUISER_ conditional around pack pragmas
|
|
* 01-29-91 GJF ANSI naming.
|
|
* 01-29-91 SRW Added call to GetFileType [_WIN32_]
|
|
* 02-18-91 SRW Removed duplicate defintion of _NFILE_ (see mtdll.h)
|
|
* 04-04-91 GJF Added definitions for _base[version|major|minor]
|
|
* (_WIN32_).
|
|
* 04-08-91 GJF Temporary hack for Win32/DOS folks - added HeapDestroy
|
|
* call to doexit to tear down the OS heap used by C
|
|
* heap.
|
|
* 04-09-91 PNT Added _MAC_ conditional
|
|
* 04-26-91 SRW Removed level 3 warnings
|
|
* 07-16-91 GJF Added fp initialization test-and-call [_WIN32_].
|
|
* 07-26-91 GJF Revised initialization and termination stuff. In
|
|
* particular, removed need for win32ini.c [_WIN32_].
|
|
* 08-07-91 GJF Added init. for FORTRAN runtime, if present [_WIN32_].
|
|
* 08-21-91 GJF Test _prmtmp against NULL, not _prmtmp().
|
|
* 08-21-91 JCR Added _exitflag, _endstdio, _cpumode, etc.
|
|
* 09-09-91 GJF Revised _doinitterm for C++ init. support and to make
|
|
* _onexit/atexit compatible with C++ needs.
|
|
* 09-16-91 GJF Must test __onexitend before calling _doinitterm.
|
|
* 10-29-91 GJF Force in floating point initialization for MIPS
|
|
* compiler [_WIN32_].
|
|
* 11-13-91 GJF FORTRAN needs _onexit/atexit init. before call thru
|
|
* _pFFinit.
|
|
* 12-29-91 RID MAC module created, based on OS2 version
|
|
* 01-10-92 GJF Merged. Also, added _C_Termination_Done [_WIN32_].
|
|
* 02-13-92 GJF Moved all lowio initialization to ioinit.c for Win32.
|
|
* 03-12-92 SKS Major changes to initialization/termination scheme
|
|
* 04-01-92 XY implemented new intialization/termination schema (MAC)
|
|
* 04-16-92 DJM POSIX support.
|
|
* 04-17-92 SKS Export _initterm() for CRTDLL model
|
|
* 05-07-92 DJM Removed _exit() from POSIX build.
|
|
* 06-03-92 GJF Temporarily restored call to FORTRAN init.
|
|
* 08-26-92 SKS Add _osver, _winver, _winmajor, _winminor
|
|
* 08-28-92 GJF Use unistd.h for POSIX build.
|
|
* 09-02-92 SKS Fix _onexit table traversal to be LIFO.
|
|
* Since table is built forward (my changes 03-12-92)
|
|
* the table must be traversed in reverse order.
|
|
* 11-12-92 SKS Remove hard-coded call to FORTRAN initializer
|
|
* 03-20-93 SKS Remove obsolete variables _osmode, _cpumode, etc.
|
|
* 04-06-93 SKS Replace _CRTAPI* with __cdecl
|
|
* 04-07-93 SKS Change to __declspec(dllexport) for CRT DLL model
|
|
* 04-19-93 SKS Remove obsolete variable _child.
|
|
* 04-20-93 SKS _C_Termination_Done is now used by DLLs in LIBC/LIBCMT
|
|
* models, not just in MSVCRT10.DLL.
|
|
* 07-16-93 SRW ALPHA Merge
|
|
* 09-21-93 CFW Move _initmbctable call to _cinit().
|
|
* 10-19-93 GJF Merged NT and Cuda versions. Cleaned out a lot of old
|
|
* Cruiser and Dosx32 support. Replaced MTHREAD with
|
|
* _MT, _MIPS_ with _M_MRX000, _ALPHA_ with _M_ALPHA.
|
|
* 11-09-93 GJF Moved _initmbctable call back into crt0.c.
|
|
* 11-09-93 GJF Replaced PF with _PVFV (defined in internal.h).
|
|
* 11-19-93 CFW Add _wargv, _wpgmptr.
|
|
* 11-29-93 CFW Add _wenviron.
|
|
* 12-15-93 CFW Set _pgmptr, _wpgmptr to NULL (MIPS compiler bug).
|
|
* 02-04-94 CFW Add _[w]initenv.
|
|
* 03-25-94 GJF Made definitions of:
|
|
* __argc,
|
|
* __argv, __wargv,
|
|
* _C_Termination_Done,
|
|
* _environ, _wenviron,
|
|
* _exitflag,
|
|
* __initenv, __winitenv,
|
|
* __onexitbegin, __onexitend,
|
|
* _osver,
|
|
* _pgmptr, _wpgmptr,
|
|
* _winmajor,
|
|
* _winminor
|
|
* _winver
|
|
* conditional on ndef DLL_FOR_WIN32S.
|
|
* 10-02-94 BWT Add PPC changes
|
|
* 12-03-94 SKS Clean up OS/2 references
|
|
* 02-11-95 CFW PPC -> _M_MPPC.
|
|
* 02-16-95 JWM Spliced _WIN32 & Mac versions.
|
|
* 02-24-95 CFW Call _CrtDumpMemoryLeaks.
|
|
* 02-27-95 CFW Make _CrtDumpMemoryLeaks call conditional
|
|
* 04-06-95 CFW Only check for static libs, avoid infinite loop.
|
|
* 07-24-95 CFW Call _CrtDumpMemoryLeaks for PMac.
|
|
* 12-18-95 JWM doexit() can no longer be called recursively.
|
|
* 08-01-96 RDK For PMac, define _osflagfiles, cleaned up Gestalt test,
|
|
* and make termination parallel x86 functionality.
|
|
* 07-24-97 GJF Added __env_initialized flag.
|
|
* 08-06-97 GJF Moved __mbctype_initialized flag here from crt0.c.
|
|
* 09-26-97 BWT Fix POSIX
|
|
* 10-07-97 RDL Added IA64.
|
|
* 01-16-98 RDL Removed IA64 from fpinit #if for _fltused support.
|
|
* 10-02-98 GJF Added _osplatform.
|
|
* 11-13-98 KBF Only do an atexit(RTC_Terminate) - moved Init to after
|
|
* _heap_init
|
|
* 02-01-99 GJF Slight change to terminator execution loop to allow
|
|
* terminators to register more terminators.
|
|
* 04-28-99 PML Wrap __declspec(allocate()) in _CRTALLOC macro.
|
|
* 05-11-99 KBF Wrap RTC support in #ifdef.
|
|
* 05-17-99 PML Remove all Macintosh support.
|
|
* 03-06-00 PML Add __crtExitProcess for COM+ exit processing.
|
|
* 04-28-00 BWT Fix Posix
|
|
* 08-04-00 PML COM+ -> managed (VS7#117746).
|
|
* 03-27-01 PML .CRT$XI funcs now return an error status (VS7#231220).
|
|
* 05-01-01 BWT Remove TerminateProcess call. It's not necessary when
|
|
* simply exiting would serve the same purpose.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include <cruntime.h>
|
|
#ifdef _POSIX_
|
|
#include <unistd.h>
|
|
#else
|
|
#include <msdos.h>
|
|
#include <rtcapi.h>
|
|
#endif
|
|
#include <dos.h>
|
|
#include <oscalls.h>
|
|
#include <mtdll.h>
|
|
#include <internal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <process.h>
|
|
#include <dbgint.h>
|
|
#include <sect_attribs.h>
|
|
|
|
/* define errno */
|
|
#ifndef _MT
|
|
int errno = 0; /* libc error value */
|
|
unsigned long _doserrno = 0; /* OS system error value */
|
|
#endif /* _MT */
|
|
|
|
|
|
/* define umask */
|
|
int _umaskval = 0;
|
|
|
|
|
|
/* define version info variables */
|
|
|
|
_CRTIMP unsigned int _osplatform = 0;
|
|
_CRTIMP unsigned int _osver = 0;
|
|
_CRTIMP unsigned int _winver = 0;
|
|
_CRTIMP unsigned int _winmajor = 0;
|
|
_CRTIMP unsigned int _winminor = 0;
|
|
|
|
|
|
/* argument vector and environment */
|
|
|
|
_CRTIMP int __argc = 0;
|
|
_CRTIMP char **__argv = NULL;
|
|
_CRTIMP wchar_t **__wargv = NULL;
|
|
#ifdef _POSIX_
|
|
char **environ = NULL;
|
|
#else
|
|
_CRTIMP char **_environ = NULL;
|
|
_CRTIMP char **__initenv = NULL;
|
|
_CRTIMP wchar_t **_wenviron = NULL;
|
|
_CRTIMP wchar_t **__winitenv = NULL;
|
|
#endif
|
|
_CRTIMP char *_pgmptr = NULL; /* ptr to program name */
|
|
_CRTIMP wchar_t *_wpgmptr = NULL; /* ptr to wide program name */
|
|
|
|
|
|
/* callable exit flag */
|
|
char _exitflag = 0;
|
|
|
|
/*
|
|
* flag indicating if C runtime termination has been done. set if exit,
|
|
* _exit, _cexit or _c_exit has been called. checked when _CRTDLL_INIT
|
|
* is called with DLL_PROCESS_DETACH.
|
|
*/
|
|
int _C_Termination_Done = FALSE;
|
|
int _C_Exit_Done = FALSE;
|
|
|
|
#ifndef CRTDLL
|
|
/*
|
|
* Flag checked by getenv() and _putenv() to determine if the environment has
|
|
* been initialized.
|
|
*/
|
|
int __env_initialized;
|
|
|
|
#endif
|
|
|
|
#ifdef _MBCS
|
|
/*
|
|
* Flag to ensure multibyte ctype table is only initialized once
|
|
*/
|
|
int __mbctype_initialized;
|
|
#endif /* _MBCS */
|
|
|
|
|
|
/*
|
|
* NOTE: THE USE OF THE POINTERS DECLARED BELOW DEPENDS ON THE PROPERTIES
|
|
* OF C COMMUNAL VARIABLES. SPECIFICALLY, THEY ARE NON-NULL IFF THERE EXISTS
|
|
* A DEFINITION ELSEWHERE INITIALIZING THEM TO NON-NULL VALUES.
|
|
*/
|
|
|
|
/*
|
|
* pointers to initialization functions
|
|
*/
|
|
|
|
_PVFV _FPinit; /* floating point init. */
|
|
|
|
/*
|
|
* pointers to initialization sections
|
|
*/
|
|
|
|
extern _CRTALLOC(".CRT$XIA") _PIFV __xi_a[];
|
|
extern _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[]; /* C initializers */
|
|
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
|
|
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[]; /* C++ initializers */
|
|
extern _CRTALLOC(".CRT$XPA") _PVFV __xp_a[];
|
|
extern _CRTALLOC(".CRT$XPZ") _PVFV __xp_z[]; /* C pre-terminators */
|
|
extern _CRTALLOC(".CRT$XTA") _PVFV __xt_a[];
|
|
extern _CRTALLOC(".CRT$XTZ") _PVFV __xt_z[]; /* C terminators */
|
|
|
|
#if defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) || defined(_M_IA64)
|
|
/*
|
|
* For MIPS compiler, must explicitly force in and call the floating point
|
|
* initialization.
|
|
*/
|
|
extern void __cdecl _fpmath(void);
|
|
#endif
|
|
|
|
/*
|
|
* pointers to the start and finish of the _onexit/atexit table
|
|
*/
|
|
_PVFV *__onexitbegin;
|
|
_PVFV *__onexitend;
|
|
|
|
|
|
/*
|
|
* static (internal) functions that walk a table of function pointers,
|
|
* calling each entry between the two pointers, skipping NULL entries
|
|
*
|
|
* _initterm needs to be exported for CRT DLL so that C++ initializers in the
|
|
* client EXE / DLLs can be initialized.
|
|
*
|
|
* _initterm_e calls function pointers that return a nonzero error code to
|
|
* indicate an initialization failed fatally.
|
|
*/
|
|
#ifdef CRTDLL
|
|
void __cdecl _initterm(_PVFV *, _PVFV *);
|
|
#else
|
|
static void __cdecl _initterm(_PVFV *, _PVFV *);
|
|
#endif
|
|
static int __cdecl _initterm_e(_PIFV *, _PIFV *);
|
|
|
|
|
|
/***
|
|
*_cinit - C initialization
|
|
*
|
|
*Purpose:
|
|
* This routine performs the shared DOS and Windows initialization.
|
|
* The following order of initialization must be preserved -
|
|
*
|
|
* 1. Check for devices for file handles 0 - 2
|
|
* 2. Integer divide interrupt vector setup
|
|
* 3. General C initializer routines
|
|
*
|
|
*Entry:
|
|
* No parameters: Called from __crtstart and assumes data
|
|
* set up correctly there.
|
|
*
|
|
*Exit:
|
|
* Initializes C runtime data.
|
|
* Returns 0 if all .CRT$XI internal initializations succeeded, else
|
|
* the _RT_* fatal error code encountered.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
int __cdecl _cinit (
|
|
void
|
|
)
|
|
{
|
|
int initret;
|
|
|
|
/*
|
|
* initialize floating point package, if present
|
|
*/
|
|
#if defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC)
|
|
/*
|
|
* MIPS compiler doesn't emit external reference to _fltused. Therefore,
|
|
* must always force in the floating point initialization.
|
|
*/
|
|
_fpmath();
|
|
#else
|
|
if ( _FPinit != NULL )
|
|
(*_FPinit)();
|
|
#endif
|
|
|
|
/*
|
|
* do initializations
|
|
*/
|
|
initret = _initterm_e( __xi_a, __xi_z );
|
|
if ( initret != 0 )
|
|
return initret;
|
|
|
|
#ifdef _RTC
|
|
atexit(_RTC_Terminate);
|
|
#endif
|
|
/*
|
|
* do C++ initializations
|
|
*/
|
|
_initterm( __xc_a, __xc_z );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***
|
|
*exit(status), _exit(status), _cexit(void), _c_exit(void) - C termination
|
|
*
|
|
*Purpose:
|
|
*
|
|
* Entry points:
|
|
*
|
|
* exit(code): Performs all the C termination functions
|
|
* and terminates the process with the return code
|
|
* supplied by the user.
|
|
*
|
|
* _exit(code): Performs a quick exit routine that does not
|
|
* do certain 'high-level' exit processing. The _exit
|
|
* routine terminates the process with the return code
|
|
* supplied by the user.
|
|
*
|
|
* _cexit(): Performs the same C lib termination processing
|
|
* as exit(code) but returns control to the caller
|
|
* when done (i.e., does NOT terminate the process).
|
|
*
|
|
* _c_exit(): Performs the same C lib termination processing
|
|
* as _exit(code) but returns control to the caller
|
|
* when done (i.e., does NOT terminate the process).
|
|
*
|
|
* Termination actions:
|
|
*
|
|
* exit(), _cexit():
|
|
*
|
|
* 1. call user's terminator routines
|
|
* 2. call C runtime preterminators
|
|
*
|
|
* _exit(), _c_exit():
|
|
*
|
|
* 3. call C runtime terminators
|
|
* 4. return to DOS or caller
|
|
*
|
|
* Notes:
|
|
*
|
|
* The termination sequence is complicated due to the multiple entry
|
|
* points sharing the common code body while having different entry/exit
|
|
* sequences.
|
|
*
|
|
* Multi-thread notes:
|
|
*
|
|
* 1. exit() should NEVER be called when mthread locks are held.
|
|
* The exit() routine can make calls that try to get mthread locks.
|
|
*
|
|
* 2. _exit()/_c_exit() can be called from anywhere, with or without locks held.
|
|
* Thus, _exit() can NEVER try to get locks (otherwise, deadlock
|
|
* may occur). _exit() should always 'work' (i.e., the process
|
|
* should always terminate successfully).
|
|
*
|
|
* 3. Only one thread is allowed into the exit code (see _lockexit()
|
|
* and _unlockexit() routines).
|
|
*
|
|
*Entry:
|
|
* exit(), _exit()
|
|
* int status - exit status (0-255)
|
|
*
|
|
* _cexit(), _c_exit()
|
|
* <no input>
|
|
*
|
|
*Exit:
|
|
* exit(), _exit()
|
|
* <EXIT to DOS>
|
|
*
|
|
* _cexit(), _c_exit()
|
|
* Return to caller
|
|
*
|
|
*Uses:
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
/* worker routine prototype */
|
|
static void __cdecl doexit (int code, int quick, int retcaller);
|
|
|
|
void __cdecl exit (
|
|
int code
|
|
)
|
|
{
|
|
doexit(code, 0, 0); /* full term, kill process */
|
|
}
|
|
|
|
#ifndef _POSIX_
|
|
|
|
void __cdecl _exit (
|
|
int code
|
|
)
|
|
{
|
|
doexit(code, 1, 0); /* quick term, kill process */
|
|
}
|
|
|
|
void __cdecl _cexit (
|
|
void
|
|
)
|
|
{
|
|
doexit(0, 0, 1); /* full term, return to caller */
|
|
}
|
|
|
|
void __cdecl _c_exit (
|
|
void
|
|
)
|
|
{
|
|
doexit(0, 1, 1); /* quick term, return to caller */
|
|
}
|
|
|
|
#endif /* _POSIX_ */
|
|
|
|
|
|
static void __cdecl doexit (
|
|
int code,
|
|
int quick,
|
|
int retcaller
|
|
)
|
|
{
|
|
#ifdef _DEBUG
|
|
static int fExit = 0;
|
|
#endif /* _DEBUG */
|
|
|
|
#ifdef _MT
|
|
_lockexit(); /* assure only 1 thread in exit path */
|
|
#endif
|
|
|
|
#ifndef _POSIX_
|
|
if (_C_Exit_Done == TRUE) /* if doexit() is being called recursively */
|
|
goto ExitBranch;
|
|
#endif
|
|
_C_Termination_Done = TRUE;
|
|
|
|
/* save callable exit flag (for use by terminators) */
|
|
_exitflag = (char) retcaller; /* 0 = term, !0 = callable exit */
|
|
|
|
if (!quick) {
|
|
|
|
/*
|
|
* do _onexit/atexit() terminators
|
|
* (if there are any)
|
|
*
|
|
* These terminators MUST be executed in reverse order (LIFO)!
|
|
*
|
|
* NOTE:
|
|
* This code assumes that __onexitbegin points
|
|
* to the first valid onexit() entry and that
|
|
* __onexitend points past the last valid entry.
|
|
* If __onexitbegin == __onexitend, the table
|
|
* is empty and there are no routines to call.
|
|
*/
|
|
|
|
if (__onexitbegin) {
|
|
while ( --__onexitend >= __onexitbegin )
|
|
/*
|
|
* if current table entry is non-NULL,
|
|
* call thru it.
|
|
*/
|
|
if ( *__onexitend != NULL )
|
|
(**__onexitend)();
|
|
}
|
|
|
|
/*
|
|
* do pre-terminators
|
|
*/
|
|
_initterm(__xp_a, __xp_z);
|
|
}
|
|
|
|
/*
|
|
* do terminators
|
|
*/
|
|
_initterm(__xt_a, __xt_z);
|
|
|
|
#ifndef CRTDLL
|
|
#ifdef _DEBUG
|
|
/* Dump all memory leaks */
|
|
if (!fExit && _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_LEAK_CHECK_DF)
|
|
{
|
|
fExit = 1;
|
|
_CrtDumpMemoryLeaks();
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef _POSIX_
|
|
ExitBranch:
|
|
#endif
|
|
/* return to OS or to caller */
|
|
|
|
if (retcaller) {
|
|
#ifdef _MT
|
|
_unlockexit(); /* unlock the exit code path */
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
#ifdef _POSIX_
|
|
|
|
_exit(code);
|
|
}
|
|
|
|
#else /* ndef _POSIX_ */
|
|
|
|
_C_Exit_Done = TRUE;
|
|
|
|
__crtExitProcess(code);
|
|
}
|
|
|
|
/***
|
|
* __crtExitProcess - CRT wrapper for ExitProcess
|
|
*
|
|
*Purpose:
|
|
* If we're part of a managed app, then call the CorExitProcess,
|
|
* otherwise call ExitProcess. For managed apps, calling ExitProcess can
|
|
* be problematic, because it doesn't give the managed FinalizerThread a
|
|
* chance to clean up.
|
|
*
|
|
* To determine if we're a managed app, we check if mscoree.dll is loaded.
|
|
* Then, if CorExitProcess is available, we call it.
|
|
*
|
|
*Entry:
|
|
* int status - exit code
|
|
*
|
|
*Exit:
|
|
* Does not return
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
typedef void (WINAPI * PFN_EXIT_PROCESS)(UINT uExitCode);
|
|
|
|
void __cdecl __crtExitProcess (
|
|
int status
|
|
)
|
|
{
|
|
HMODULE hmod;
|
|
PFN_EXIT_PROCESS pfn;
|
|
|
|
hmod = GetModuleHandle("mscoree.dll");
|
|
if (hmod != NULL) {
|
|
pfn = (PFN_EXIT_PROCESS)GetProcAddress(hmod, "CorExitProcess");
|
|
if (pfn != NULL) {
|
|
pfn(status);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Either mscoree.dll isn't loaded,
|
|
* or CorExitProcess isn't exported from mscoree.dll,
|
|
* or CorExitProcess returned (should never happen).
|
|
* Just call ExitProcess.
|
|
*/
|
|
|
|
ExitProcess(status);
|
|
}
|
|
|
|
#endif /* _POSIX_ */
|
|
|
|
#ifdef _MT
|
|
/***
|
|
* _lockexit - Aquire the exit code lock
|
|
*
|
|
*Purpose:
|
|
* Makes sure only one thread is in the exit code at a time.
|
|
* If a thread is already in the exit code, it must be allowed
|
|
* to continue. All other threads must pend.
|
|
*
|
|
* Notes:
|
|
*
|
|
* (1) It is legal for a thread that already has the lock to
|
|
* try and get it again(!). That is, consider the following
|
|
* sequence:
|
|
*
|
|
* (a) program calls exit()
|
|
* (b) thread locks exit code
|
|
* (c) user onexit() routine calls _exit()
|
|
* (d) same thread tries to lock exit code
|
|
*
|
|
* Since _exit() must ALWAYS be able to work (i.e., can be called
|
|
* from anywhere with no regard for locking), we must make sure the
|
|
* program does not deadlock at step (d) above.
|
|
*
|
|
* (2) If a thread executing exit() or _exit() aquires the exit lock,
|
|
* other threads trying to get the lock will pend forever. That is,
|
|
* since exit() and _exit() terminate the process, there is not need
|
|
* for them to unlock the exit code path.
|
|
*
|
|
* (3) Note that onexit()/atexit() routines call _lockexit/_unlockexit
|
|
* to protect mthread access to the onexit table.
|
|
*
|
|
* (4) The 32-bit OS semaphore calls DO allow a single thread to acquire
|
|
* the same lock multiple times* thus, this version is straight forward.
|
|
*
|
|
*Entry: <none>
|
|
*
|
|
*Exit:
|
|
* Calling thread has exit code path locked on return.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
void __cdecl _lockexit (
|
|
void
|
|
)
|
|
{
|
|
_mlock(_EXIT_LOCK1);
|
|
}
|
|
|
|
/***
|
|
* _unlockexit - Release exit code lock
|
|
*
|
|
*Purpose:
|
|
* [See _lockexit() description above.]
|
|
*
|
|
* This routine is called by _cexit(), _c_exit(), and onexit()/atexit().
|
|
* The exit() and _exit() routines never unlock the exit code path since
|
|
* they are terminating the process.
|
|
*
|
|
*Entry:
|
|
* Exit code path is unlocked.
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
void __cdecl _unlockexit (
|
|
void
|
|
)
|
|
{
|
|
_munlock(_EXIT_LOCK1);
|
|
}
|
|
|
|
#endif /* _MT */
|
|
|
|
|
|
/***
|
|
* static void _initterm(_PVFV * pfbegin, _PVFV * pfend) - call entries in
|
|
* function pointer table
|
|
*
|
|
*Purpose:
|
|
* Walk a table of function pointers, calling each entry, as follows:
|
|
*
|
|
* 1. walk from beginning to end, pfunctbl is assumed to point
|
|
* to the beginning of the table, which is currently a null entry,
|
|
* as is the end entry.
|
|
* 2. skip NULL entries
|
|
* 3. stop walking when the end of the table is encountered
|
|
*
|
|
*Entry:
|
|
* _PVFV *pfbegin - pointer to the beginning of the table (first
|
|
* valid entry).
|
|
* _PVFV *pfend - pointer to the end of the table (after last
|
|
* valid entry).
|
|
*
|
|
*Exit:
|
|
* No return value
|
|
*
|
|
*Notes:
|
|
* This routine must be exported in the CRT DLL model so that the client
|
|
* EXE and client DLL(s) can call it to initialize their C++ constructors.
|
|
*
|
|
*Exceptions:
|
|
* If either pfbegin or pfend is NULL, or invalid, all bets are off!
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef CRTDLL
|
|
void __cdecl _initterm (
|
|
#else
|
|
static void __cdecl _initterm (
|
|
#endif
|
|
_PVFV * pfbegin,
|
|
_PVFV * pfend
|
|
)
|
|
{
|
|
/*
|
|
* walk the table of function pointers from the bottom up, until
|
|
* the end is encountered. Do not skip the first entry. The initial
|
|
* value of pfbegin points to the first valid entry. Do not try to
|
|
* execute what pfend points to. Only entries before pfend are valid.
|
|
*/
|
|
while ( pfbegin < pfend )
|
|
{
|
|
/*
|
|
* if current table entry is non-NULL, call thru it.
|
|
*/
|
|
if ( *pfbegin != NULL )
|
|
(**pfbegin)();
|
|
++pfbegin;
|
|
}
|
|
}
|
|
|
|
/***
|
|
* static int _initterm_e(_PIFV * pfbegin, _PIFV * pfend) - call entries in
|
|
* function pointer table, return error code on any failure
|
|
*
|
|
*Purpose:
|
|
* Walk a table of function pointers in the same way as _initterm, but
|
|
* here the functions return an error code. If an error is returned, it
|
|
* will be a nonzero value equal to one of the _RT_* codes.
|
|
*
|
|
*Entry:
|
|
* _PIFV *pfbegin - pointer to the beginning of the table (first
|
|
* valid entry).
|
|
* _PIFV *pfend - pointer to the end of the table (after last
|
|
* valid entry).
|
|
*
|
|
*Exit:
|
|
* No return value
|
|
*
|
|
*Notes:
|
|
* This routine must be exported in the CRT DLL model so that the client
|
|
* EXE and client DLL(s) can call it.
|
|
*
|
|
*Exceptions:
|
|
* If either pfbegin or pfend is NULL, or invalid, all bets are off!
|
|
*
|
|
*******************************************************************************/
|
|
|
|
static int __cdecl _initterm_e (
|
|
_PIFV * pfbegin,
|
|
_PIFV * pfend
|
|
)
|
|
{
|
|
int ret = 0;
|
|
|
|
/*
|
|
* walk the table of function pointers from the bottom up, until
|
|
* the end is encountered. Do not skip the first entry. The initial
|
|
* value of pfbegin points to the first valid entry. Do not try to
|
|
* execute what pfend points to. Only entries before pfend are valid.
|
|
*/
|
|
while ( pfbegin < pfend && ret == 0)
|
|
{
|
|
/*
|
|
* if current table entry is non-NULL, call thru it.
|
|
*/
|
|
if ( *pfbegin != NULL )
|
|
ret = (**pfbegin)();
|
|
++pfbegin;
|
|
}
|
|
|
|
return ret;
|
|
}
|