739 lines
13 KiB
C
739 lines
13 KiB
C
/*
|
|
* serial.c - Access serialization routines module.
|
|
*/
|
|
|
|
|
|
/* Headers
|
|
**********/
|
|
|
|
#include "project.h"
|
|
#pragma hdrstop
|
|
|
|
#include "init.h"
|
|
|
|
|
|
/* Types
|
|
********/
|
|
|
|
/* process information */
|
|
|
|
typedef struct _processinfo
|
|
{
|
|
HANDLE hModule;
|
|
}
|
|
PROCESSINFO;
|
|
DECLARE_STANDARD_TYPES(PROCESSINFO);
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* debug flags */
|
|
|
|
typedef enum _serialdebugflags
|
|
{
|
|
SERIAL_DFL_BREAK_ON_PROCESS_ATTACH = 0x0001,
|
|
|
|
SERIAL_DFL_BREAK_ON_THREAD_ATTACH = 0x0002,
|
|
|
|
ALL_SERIAL_DFLAGS = (SERIAL_DFL_BREAK_ON_PROCESS_ATTACH |
|
|
SERIAL_DFL_BREAK_ON_THREAD_ATTACH)
|
|
}
|
|
SERIALDEBUGFLAGS;
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/* Module Variables
|
|
*******************/
|
|
|
|
#pragma data_seg(DATA_SEG_SHARED)
|
|
|
|
/*
|
|
* RAIDRAID: (16273) The use of Mnrcs in a shared data section is broken under
|
|
* NT. To run under NT, this code should be changed to use a shared mutex
|
|
* referenced by hMutex in Mpi.
|
|
*/
|
|
|
|
/* critical section used for access serialization */
|
|
|
|
PRIVATE_DATA NONREENTRANTCRITICALSECTION Mnrcs =
|
|
{
|
|
{ 0 },
|
|
|
|
#ifdef DEBUG
|
|
INVALID_THREAD_ID,
|
|
#endif /* DEBUG */
|
|
|
|
FALSE
|
|
};
|
|
|
|
/* number of attached processes */
|
|
|
|
PRIVATE_DATA ULONG MulcProcesses = 0;
|
|
|
|
#pragma data_seg(DATA_SEG_PER_INSTANCE)
|
|
|
|
/* information about current process */
|
|
|
|
/*
|
|
* Initialize Mpi so it is actually put in the .instanc section instead of the
|
|
* .bss section.
|
|
*/
|
|
|
|
PRIVATE_DATA PROCESSINFO Mpi =
|
|
{
|
|
NULL
|
|
};
|
|
|
|
#pragma data_seg()
|
|
|
|
#ifdef DEBUG
|
|
|
|
#pragma data_seg(DATA_SEG_SHARED)
|
|
|
|
/* debug flags */
|
|
|
|
PRIVATE_DATA DWORD MdwSerialModuleFlags = 0;
|
|
|
|
#pragma data_seg(DATA_SEG_READ_ONLY)
|
|
|
|
/* .ini file switch descriptions */
|
|
|
|
PRIVATE_DATA CBOOLINISWITCH cbisBreakOnProcessAttach =
|
|
{
|
|
IST_BOOL,
|
|
TEXT( "BreakOnProcessAttach"),
|
|
&MdwSerialModuleFlags,
|
|
SERIAL_DFL_BREAK_ON_PROCESS_ATTACH
|
|
};
|
|
|
|
PRIVATE_DATA CBOOLINISWITCH cbisBreakOnThreadAttach =
|
|
{
|
|
IST_BOOL,
|
|
TEXT("BreakOnThreadAttach"),
|
|
&MdwSerialModuleFlags,
|
|
SERIAL_DFL_BREAK_ON_THREAD_ATTACH
|
|
};
|
|
|
|
PRIVATE_DATA const PCVOID MrgcpcvisSerialModule[] =
|
|
{
|
|
&cbisBreakOnProcessAttach,
|
|
&cbisBreakOnThreadAttach
|
|
};
|
|
|
|
#pragma data_seg()
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/***************************** Private Functions *****************************/
|
|
|
|
/* Module Prototypes
|
|
********************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
PRIVATE_CODE BOOL IsValidPCSERIALCONTROL(PCSERIALCONTROL);
|
|
PRIVATE_CODE BOOL IsValidPCPROCESSINFO(PCPROCESSINFO);
|
|
PRIVATE_CODE BOOL IsValidPCCRITICAL_SECTION(PCCRITICAL_SECTION);
|
|
PRIVATE_CODE BOOL IsValidThreadId(DWORD);
|
|
PRIVATE_CODE BOOL IsValidPCNONREENTRANTCRITICALSECTION(PCNONREENTRANTCRITICALSECTION);
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
** IsValidPCSERIALCONTROL()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidPCSERIALCONTROL(PCSERIALCONTROL pcserctrl)
|
|
{
|
|
return(IS_VALID_READ_PTR(pcserctrl, CSERIALCONTROL) &&
|
|
(! pcserctrl->AttachProcess ||
|
|
IS_VALID_CODE_PTR(pcserctrl->AttachProcess, AttachProcess)) &&
|
|
(! pcserctrl->DetachProcess ||
|
|
IS_VALID_CODE_PTR(pcserctrl->DetachProcess, DetachProcess)) &&
|
|
(! pcserctrl->AttachThread ||
|
|
IS_VALID_CODE_PTR(pcserctrl->AttachThread, AttachThread)) &&
|
|
(! pcserctrl->DetachThread||
|
|
IS_VALID_CODE_PTR(pcserctrl->DetachThread, DetachThread)));
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidPCPROCESSINFO()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidPCPROCESSINFO(PCPROCESSINFO pcpi)
|
|
{
|
|
return(IS_VALID_READ_PTR(pcpi, CPROCESSINFO) &&
|
|
IS_VALID_HANDLE(pcpi->hModule, MODULE));
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidPCCRITICAL_SECTION()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidPCCRITICAL_SECTION(PCCRITICAL_SECTION pccritsec)
|
|
{
|
|
return(IS_VALID_READ_PTR(pccritsec, CCRITICAL_SECTION));
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidThreadId()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidThreadId(DWORD dwThreadId)
|
|
{
|
|
return(dwThreadId != INVALID_THREAD_ID);
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidPCNONREENTRANTCRITICALSECTION()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidPCNONREENTRANTCRITICALSECTION(
|
|
PCNONREENTRANTCRITICALSECTION pcnrcs)
|
|
{
|
|
/* bEntered may be any value. */
|
|
|
|
return(IS_VALID_READ_PTR(pcnrcs, CNONREENTRANTCRITICALSECTION) &&
|
|
IS_VALID_STRUCT_PTR(&(pcnrcs->critsec), CCRITICAL_SECTION) &&
|
|
EVAL(pcnrcs->dwOwnerThread == INVALID_THREAD_ID ||
|
|
IsValidThreadId(pcnrcs->dwOwnerThread)));
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/****************************** Public Functions *****************************/
|
|
|
|
|
|
#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
** SetSerialModuleIniSwitches()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL SetSerialModuleIniSwitches(void)
|
|
{
|
|
BOOL bResult;
|
|
|
|
bResult = SetIniSwitches(MrgcpcvisSerialModule,
|
|
ARRAY_ELEMENTS(MrgcpcvisSerialModule));
|
|
|
|
ASSERT(FLAGS_ARE_VALID(MdwSerialModuleFlags, ALL_SERIAL_DFLAGS));
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
** AttachProcess()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL AttachProcess(HMODULE hmod)
|
|
{
|
|
BOOL bResult;
|
|
|
|
ReinitializeNonReentrantCriticalSection(&Mnrcs);
|
|
|
|
bResult = EnterNonReentrantCriticalSection(&Mnrcs);
|
|
|
|
if (bResult)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
ASSERT(SetAllIniSwitches());
|
|
|
|
TRACE_OUT((TEXT("AttachProcess(): Called for module %#lx."),
|
|
hmod));
|
|
|
|
if (IS_FLAG_SET(MdwSerialModuleFlags, SERIAL_DFL_BREAK_ON_PROCESS_ATTACH))
|
|
{
|
|
WARNING_OUT((TEXT("AttachProcess(): Breaking on process attach, as requested.")));
|
|
DebugBreak();
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
Mpi.hModule = hmod;
|
|
|
|
ASSERT(MulcProcesses < ULONG_MAX);
|
|
|
|
if (! MulcProcesses++)
|
|
{
|
|
TRACE_OUT((TEXT("AttachProcess(): First process attached. Calling InitializeDLL().")));
|
|
|
|
bResult = InitializeDLL();
|
|
}
|
|
else
|
|
{
|
|
|
|
#ifdef PRIVATE_HEAP
|
|
|
|
bResult = TRUE;
|
|
|
|
#else
|
|
/*
|
|
* Initialize the per-instance memory manager heap for
|
|
* subsequent processes.
|
|
*/
|
|
|
|
bResult = InitMemoryManagerModule();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if (bResult)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(&g_cserctrl, CSERIALCONTROL));
|
|
|
|
if (g_cserctrl.AttachProcess)
|
|
bResult = g_cserctrl.AttachProcess(hmod);
|
|
}
|
|
|
|
TRACE_OUT((TEXT("AttachProcess(): There are now %lu processes attached."),
|
|
MulcProcesses));
|
|
|
|
LeaveNonReentrantCriticalSection(&Mnrcs);
|
|
}
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** DetachProcess()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL DetachProcess(HMODULE hmod)
|
|
{
|
|
BOOL bResult;
|
|
|
|
bResult = EnterNonReentrantCriticalSection(&Mnrcs);
|
|
|
|
if (bResult)
|
|
{
|
|
ASSERT(hmod == Mpi.hModule);
|
|
|
|
ASSERT(MulcProcesses > 0);
|
|
|
|
TRACE_OUT((TEXT("DetachProcess(): Called for module %#lx."),
|
|
hmod));
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(&g_cserctrl, CSERIALCONTROL));
|
|
|
|
if (g_cserctrl.DetachProcess)
|
|
bResult = g_cserctrl.DetachProcess(hmod);
|
|
|
|
if (--MulcProcesses)
|
|
{
|
|
bResult = TRUE;
|
|
|
|
#ifndef PRIVATE_HEAP
|
|
|
|
/*
|
|
* Terminate the per-instance memory manager heap.
|
|
*/
|
|
|
|
ExitMemoryManagerModule();
|
|
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
TRACE_OUT((TEXT("DetachProcess(): Last process detached. Calling TerminateDLL().")));
|
|
|
|
bResult = TerminateDLL();
|
|
}
|
|
|
|
TRACE_OUT((TEXT("DetachProcess(): There are now %lu processes attached."),
|
|
MulcProcesses));
|
|
|
|
LeaveNonReentrantCriticalSection(&Mnrcs);
|
|
}
|
|
|
|
/*
|
|
* Do not call DeleteCriticalSection(&(Mnrcs->critsec)) here, since doing so
|
|
* at the right time would require unprotected access to shared data
|
|
* MulcProcesses and Mnrcs->critsec. Assume Kernel32 will clean up
|
|
* Mnrcs->critsec for us at termination.
|
|
*/
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** AttachThread()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL AttachThread(HMODULE hmod)
|
|
{
|
|
BOOL bResult;
|
|
|
|
bResult = EnterNonReentrantCriticalSection(&Mnrcs);
|
|
|
|
if (bResult)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
ASSERT(SetAllIniSwitches());
|
|
|
|
TRACE_OUT((TEXT("AttachThread() called for module %#lx, thread ID %#lx."),
|
|
hmod,
|
|
GetCurrentThreadId()));
|
|
|
|
if (IS_FLAG_SET(MdwSerialModuleFlags, SERIAL_DFL_BREAK_ON_THREAD_ATTACH))
|
|
{
|
|
WARNING_OUT((TEXT("AttachThread(): Breaking on thread attach, as requested.")));
|
|
DebugBreak();
|
|
}
|
|
|
|
#endif
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(&g_cserctrl, CSERIALCONTROL));
|
|
|
|
if (g_cserctrl.AttachThread)
|
|
bResult = g_cserctrl.AttachThread(hmod);
|
|
else
|
|
bResult = TRUE;
|
|
|
|
LeaveNonReentrantCriticalSection(&Mnrcs);
|
|
}
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** DetachThread()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL DetachThread(HMODULE hmod)
|
|
{
|
|
BOOL bResult;
|
|
|
|
bResult = EnterNonReentrantCriticalSection(&Mnrcs);
|
|
|
|
if (bResult)
|
|
{
|
|
TRACE_OUT((TEXT("DetachThread() called for module %#lx, thread ID %#lx."),
|
|
hmod,
|
|
GetCurrentThreadId()));
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(&g_cserctrl, CSERIALCONTROL));
|
|
|
|
if (g_cserctrl.DetachThread)
|
|
bResult = g_cserctrl.DetachThread(hmod);
|
|
else
|
|
bResult = TRUE;
|
|
|
|
LeaveNonReentrantCriticalSection(&Mnrcs);
|
|
}
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
#pragma warning(default:4100) /* "unreferenced formal parameter" warning */
|
|
|
|
|
|
/*
|
|
** ReinitializeNonReentrantCriticalSection()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void ReinitializeNonReentrantCriticalSection(
|
|
PNONREENTRANTCRITICALSECTION pnrcs)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pnrcs, CNONREENTRANTCRITICALSECTION));
|
|
|
|
InitializeCriticalSection(&(pnrcs->critsec));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** EnterNonReentrantCriticalSection()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL EnterNonReentrantCriticalSection(
|
|
PNONREENTRANTCRITICALSECTION pnrcs)
|
|
{
|
|
BOOL bEntered;
|
|
|
|
#ifdef DEBUG
|
|
|
|
BOOL bBlocked;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pnrcs, CNONREENTRANTCRITICALSECTION));
|
|
|
|
/* Is the critical section already owned by another thread? */
|
|
|
|
/* Use pnrcs->bEntered and pnrcs->dwOwnerThread unprotected here. */
|
|
|
|
bBlocked = (pnrcs->bEntered &&
|
|
GetCurrentThreadId() != pnrcs->dwOwnerThread);
|
|
|
|
if (bBlocked)
|
|
WARNING_OUT((TEXT("EnterNonReentrantCriticalSection(): Blocking thread %lx. Critical section is already owned by thread %#lx."),
|
|
GetCurrentThreadId(),
|
|
pnrcs->dwOwnerThread));
|
|
|
|
#endif
|
|
|
|
EnterCriticalSection(&(pnrcs->critsec));
|
|
|
|
bEntered = (! pnrcs->bEntered);
|
|
|
|
if (bEntered)
|
|
{
|
|
pnrcs->bEntered = TRUE;
|
|
|
|
#ifdef DEBUG
|
|
|
|
pnrcs->dwOwnerThread = GetCurrentThreadId();
|
|
|
|
if (bBlocked)
|
|
WARNING_OUT((TEXT("EnterNonReentrantCriticalSection(): Unblocking thread %lx. Critical section is now owned by this thread."),
|
|
pnrcs->dwOwnerThread));
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
LeaveCriticalSection(&(pnrcs->critsec));
|
|
|
|
ERROR_OUT((TEXT("EnterNonReentrantCriticalSection(): Thread %#lx attempted to reenter non-reentrant code."),
|
|
GetCurrentThreadId()));
|
|
}
|
|
|
|
return(bEntered);
|
|
}
|
|
|
|
|
|
/*
|
|
** LeaveNonReentrantCriticalSection()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void LeaveNonReentrantCriticalSection(
|
|
PNONREENTRANTCRITICALSECTION pnrcs)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pnrcs, CNONREENTRANTCRITICALSECTION));
|
|
|
|
if (EVAL(pnrcs->bEntered))
|
|
{
|
|
pnrcs->bEntered = FALSE;
|
|
#ifdef DEBUG
|
|
pnrcs->dwOwnerThread = INVALID_THREAD_ID;
|
|
#endif
|
|
|
|
LeaveCriticalSection(&(pnrcs->critsec));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
** NonReentrantCriticalSectionIsOwned()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL NonReentrantCriticalSectionIsOwned(
|
|
PCNONREENTRANTCRITICALSECTION pcnrcs)
|
|
{
|
|
return(pcnrcs->bEntered);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
** BeginExclusiveAccess()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL BeginExclusiveAccess(void)
|
|
{
|
|
return(EnterNonReentrantCriticalSection(&Mnrcs));
|
|
}
|
|
|
|
|
|
/*
|
|
** EndExclusiveAccess()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void EndExclusiveAccess(void)
|
|
{
|
|
LeaveNonReentrantCriticalSection(&Mnrcs);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
** AccessIsExclusive()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL AccessIsExclusive(void)
|
|
{
|
|
return(NonReentrantCriticalSectionIsOwned(&Mnrcs));
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/*
|
|
** GetThisModulesHandle()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE HMODULE GetThisModulesHandle(void)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR((PCPROCESSINFO)&Mpi, CPROCESSINFO));
|
|
|
|
return(Mpi.hModule);
|
|
}
|
|
|
|
|