?? mlock.c
字號:
/***
*mlock.c - Multi-thread locking routines
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*
*******************************************************************************/
#include <cruntime.h>
#include <oscalls.h>
#include <internal.h>
#include <mtdll.h>
#include <rterr.h>
#include <stddef.h>
#include <malloc.h>
#include <limits.h>
#include <stdio.h>
#include <dbgint.h>
#include <errno.h>
#include <winheap.h>
/*
* Local routines
*/
void __cdecl _lockerr_exit(char *);
/*
* Global Data
*/
/*
* Statically allocated critical section structures for all preallocated locks.
* These are most of the named locks before _STREAM_LOCKS, along with the locks
* for stdin/stdout/stderr. These must be preallocated so we do not hit fatal
* memory conditions on failing to initialize a critical section, except at
* runtime startup, since these locks may be taken where we have no good way
* to return a non-fatal error.
*/
#define NUM_STD_FILE_LOCKS 3
/*
* _DEBUG_LOCK is preallocated in _DEBUG & not in Retail
*/
#ifdef _DEBUG
#define NUM_NON_PREALLOC_LOCKS 4
#else /* _DEBUG */
#define NUM_NON_PREALLOC_LOCKS 5
#endif /* _DEBUG */
#define NUM_PREALLOC_LOCKS \
( _STREAM_LOCKS + NUM_STD_FILE_LOCKS - NUM_NON_PREALLOC_LOCKS )
static CRITICAL_SECTION lclcritsects[NUM_PREALLOC_LOCKS];
/*
* Lock Table
* This table contains a pointer to the critical section management structure
* for each lock.
*
* Locks marked lkPrealloc have their critical sections statically allocated
* and initialized at startup in _mtinitlocks. Locks marked lkNormal must
* be allocated when first used, via a call to _mtinitlocknum.
*/
static struct {
PCRITICAL_SECTION lock;
enum { lkNormal = 0, lkPrealloc, lkDeleted } kind;
} _locktable[_TOTAL_LOCKS] = {
{ NULL, lkPrealloc }, /* 0 == _SIGNAL_LOCK */
{ NULL, lkPrealloc }, /* 1 == _IOB_SCAN_LOCK */
{ NULL, lkNormal }, /* 2 == _TMPNAM_LOCK - not preallocated */
{ NULL, lkPrealloc }, /* 3 == _CONIO_LOCK */
{ NULL, lkPrealloc }, /* 4 == _HEAP_LOCK */
{ NULL, lkNormal }, /* 5 == _UNDNAME_LOCK - not preallocated */
{ NULL, lkPrealloc }, /* 6 == _TIME_LOCK */
{ NULL, lkPrealloc }, /* 7 == _ENV_LOCK */
{ NULL, lkPrealloc }, /* 8 == _EXIT_LOCK1 */
{ NULL, lkNormal }, /* 9 == _POPEN_LOCK - not preallocated */
{ NULL, lkPrealloc }, /* 10 == _LOCKTAB_LOCK */
{ NULL, lkNormal }, /* 11 == _OSFHND_LOCK - not preallocated */
{ NULL, lkPrealloc }, /* 12 == _SETLOCALE_LOCK */
{ NULL, lkPrealloc }, /* 13 == _MB_CP_LOCK */
{ NULL, lkPrealloc }, /* 14 == _TYPEINFO_LOCK */
#ifdef _DEBUG
{ NULL, lkPrealloc }, /* 15 == _DEBUG_LOCK */
#else /* _DEBUG */
{ NULL, lkNormal }, /* 15 == _DEBUG_LOCK */
#endif /* _DEBUG */
{ NULL, lkPrealloc }, /* 16 == _STREAM_LOCKS+0 - stdin */
{ NULL, lkPrealloc }, /* 17 == _STREAM_LOCKS+1 - stdout */
{ NULL, lkPrealloc }, /* 18 == _STREAM_LOCKS+2 - stderr */
/* { NULL, lkNormal }, /* ... */
};
#ifdef _M_IX86
#pragma optimize("y",off)
#endif /* _M_IX86 */
/***
*_mtinitlocks() - Initialize multi-thread lock scheme
*
*Purpose:
* Perform whatever initialization is required for the multi-thread
* locking (synchronization) scheme. This routine should be called
* exactly once, during startup, and this must be before any requests
* are made to assert locks.
*
* NOTES: In Win32, the multi-thread locks are created individually,
* each upon its first use. That is when any particular lock is asserted
* for the first time, the underlying critical section is then allocated,
* initialized and (finally) entered. This allocation and initialization
* is protected under _LOCKTAB_LOCK. It is _mtinitlocks' job to set up
* _LOCKTAB_LOCK.
*
* All other named (non-FILE) locks are also preallocated in _mtinitlocks.
* That is because a failure to allocate a lock on its first use in _lock
* triggers a fatal error, which cannot be permitted since that can bring
* down a long-lived app without warning.
*
*Entry:
* <none>
*
*Exit:
* returns FALSE on failure
*
*Exceptions:
*
*******************************************************************************/
int __cdecl _mtinitlocks (
void
)
{
int locknum;
int idxPrealloc = 0;
/*
* Scan _locktable[] and allocate all entries marked lkPrealloc.
*/
for ( locknum = 0 ; locknum < _TOTAL_LOCKS ; locknum++ ) {
if ( _locktable[locknum].kind == lkPrealloc ) {
_locktable[locknum].lock = &lclcritsects[idxPrealloc++];
if ( !InitializeCriticalSectionAndSpinCount( _locktable[locknum].lock,
_CRT_SPINCOUNT ))
{
_locktable[locknum].lock = NULL;
return FALSE;
}
}
}
return TRUE;
}
/***
*_mtdeletelocks() - Delete all initialized locks
*
*Purpose:
* Walks _locktable[] and _lockmap, and deletes every 'lock' (i.e.,
* critical section) which has been initialized.
*
* This function is intended for use in DLLs containing the C runtime
* (i.e., crtdll.dll and user DLLs built using libcmt.lib and the
* special startup objects). It is to be called from within the DLL's
* entrypoint function when that function is called with
* DLL_PROCESS_DETACH.
*
*Entry:
* <none>
*
*Exit:
*
*Exceptions:
* behavior undefined/unknown if a lock is being held when this routine
* is called.
*
*******************************************************************************/
void __cdecl _mtdeletelocks(
void
)
{
int locknum;
/*
* Delete and free all normal locks that have been created.
*/
for ( locknum = 0 ; locknum < _TOTAL_LOCKS ; locknum++ ) {
if ( _locktable[locknum].lock != NULL &&
_locktable[locknum].kind != lkPrealloc )
{
PCRITICAL_SECTION pcs = _locktable[locknum].lock;
DeleteCriticalSection(pcs);
/*
* Free the memory for the CritSect after deleting it.
*/
_free_crt(pcs);
_locktable[locknum].lock = NULL;
}
}
/*
* Delete all preallocated locks after all normal ones are
* freed (so preallocated _HEAP_LOCK outlives all heap usages).
*/
for ( locknum = 0 ; locknum < _TOTAL_LOCKS ; locknum++ ) {
if ( _locktable[locknum].lock != NULL &&
_locktable[locknum].kind == lkPrealloc )
{
PCRITICAL_SECTION pcs = _locktable[locknum].lock;
DeleteCriticalSection(pcs);
}
}
}
/***
* _mtinitlocknum - Allocate a non-preallocated multi-thread lock
*
*Purpose:
* Allocate a new, non-preallocated multi-thread lock. This should be
* used whenever a new lock is known to be needed, so that failure to
* allocate can return an error, instead of allowing _lock() to issue
* a fatal _RT_LOCK instead.
*
* It is not an error to call this on a normal lock which has already
* been allocated. It is used to ensure that certain named locks which
* are not preallocated are available.
*
* It is also called by _lock, in case any other paths exist which call
* _lock without calling _mtinitlocknum first. This is not expected,
* and can allow fatal _RT_LOCK errors to be issued.
*
* Since a failure sets errno to ENOMEM, this should only be called
* after the per-thread data has been set up (after _mtinit).
*
*Entry:
* locknum = number of the lock to aquire
*
*Exit:
* Returns FALSE on failure, and sets errno to ENOMEM.
*
*Exceptions:
*
*******************************************************************************/
int __cdecl _mtinitlocknum (
int locknum
)
{
PCRITICAL_SECTION pcs;
int retval=TRUE;
/*
* Check if CRT is initialized. The check if _crtheap is initialized
* will do the job. More over we had to add this test in initlocks because
* in debug version we don't endup calling lock before calling malloc_base,
* where we check for crtheap.
*/
if (_crtheap == 0) {
_FF_MSGBANNER(); /* write run-time error banner */
_NMSG_WRITE(_RT_CRT_NOTINIT); /* write message */
__crtExitProcess(255); /* normally _exit(255) */
}
if ( _locktable[locknum].lock != NULL )
return TRUE;
if ( (pcs = _malloc_crt(sizeof(CRITICAL_SECTION))) == NULL ) {
errno = ENOMEM;
return FALSE;
}
_mlock(_LOCKTAB_LOCK);
__TRY
if ( _locktable[locknum].lock == NULL ) {
if ( !InitializeCriticalSectionAndSpinCount(pcs, _CRT_SPINCOUNT) ) {
_free_crt(pcs);
errno = ENOMEM;
retval=FALSE;
} else {
_locktable[locknum].lock = pcs;
}
}
else {
_free_crt(pcs);
}
__FINALLY
_munlock(_LOCKTAB_LOCK);
__END_TRY_FINALLY
return retval;
}
/***
* _lock - Acquire a multi-thread lock
*
*Purpose:
* Acquire a multi-thread lock. If the lock has not already been
* allocated, do so, but that is an internal CRT error, since all locks
* should be allocated before first being acquired, either in
* _mtinitlocks or individually in _mtinitlocknum.
*
* Note that it is legal for a thread to aquire _EXIT_LOCK1
* multiple times.
*
*Entry:
* locknum = number of the lock to aquire
*
*Exit:
*
*Exceptions:
* A failure to allocate a new lock results in a fatal _RT_LOCK error.
*
*******************************************************************************/
void __cdecl _lock (
int locknum
)
{
/*
* Create/open the lock, if necessary
*/
if ( _locktable[locknum].lock == NULL ) {
if ( !_mtinitlocknum(locknum) )
_amsg_exit( _RT_LOCK );
}
/*
* Enter the critical section.
*/
EnterCriticalSection( _locktable[locknum].lock );
}
/***
* _unlock - Release multi-thread lock
*
*Purpose:
* Note that it is legal for a thread to aquire _EXIT_LOCK1
* multiple times.
*
*Entry:
* locknum = number of the lock to release
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/
void __cdecl _unlock (
int locknum
)
{
/*
* leave the critical section.
*/
LeaveCriticalSection( _locktable[locknum].lock );
}
#ifdef _M_IX86
#pragma optimize("y",on)
#endif /* _M_IX86 */
/***
*_lockerr_exit() - Write error message and die
*
*Purpose:
* Attempt to write out the unexpected lock error message, then terminate
* the program by a direct API call. This function is used in place of
* amsg_exit(_RT_LOCK) when it is judged unsafe to allow further lock
* or unlock calls.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/
void __cdecl _lockerr_exit (
char *msg
)
{
FatalAppExit(0, msg); /* Die with message box */
__crtExitProcess(255); /* Just die */
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -