text: /*
   * 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);
  }
  
  
  