windows-nt/Source/XPSP1/NT/enduser/netmeeting/t120/mst123/tprtintf.cpp
2020-09-26 16:20:57 +08:00

1075 lines
36 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "precomp.h"
/* tprtintf.cpp
*
* Copyright (c) 1996 by Microsoft Corporation
*
* Abstract:
* This is the implementation file for the Win32 PSTN transport stack.
* This file contains all of the public functions needed to use
* the PSTN stack. Transprt.cpp really performs three functions:
*
* 1. Since this is a Win32 DLL, it provides a C to C++ conversion
* layer. The functions called by the user are not processed
* in this file, they are passed on down to the C++ object that
* handles it (g_pController). If we supplied a C++
* interface to the user, it would force the user to use C++ and
* it would force them to use a C++ compiler that used the same
* name-mangling algorithm as our compiler(s).
*
* 2. It holds the code that acts as the administrator for the PSTN
* thread. This is a fairly straightforward piece of code that
* is explained later in this comment block.
*
* 3. This file provides thread synchronization to the underlying
* code by protecting it with a CRITICAL_SECTION. This prevents
* multiple threads from executing the code at the same time.
*
*
*
* CREATING A SECONDARY "PSTN" THREAD
*
* Since this is a Win32 DLL, we create a seperate thread to maintain the
* PSTN DLL. During initialization of the DLL, we create the secondary or
* PSTN thread which runs in response to timeouts and system events. It
* uses the WaitForMultipleObjects() system call to wait for events that
* need to be processed.
*
* This DLL is event driven. When a new com port is opened and
* initialized, it uses Win32 event objects to signal significant events.
* Three events that can occur are read events, write events, and control
* events. An example of a control event is a change in the carrier
* detect signal.
*
* When the ComPort object initializes an event object, it registers the
* object with the PSTN thread, by placing it in the PSTN Event_List. It
* not only registers the event, but also all of the data needed to
* identify the event. The following structure must be filled out by the
* ComPort for each event that it registers.
* struct
* {
* HANDLE event; // event object
* WIN32Event event_type; // READ, WRITE, CONTROL
* BOOL delete_event; // FLAG, delete it?
* PhysicalHandle physical_handle; // Handle associated with
* // ComPort
* IObject * physical_layer; // this pointer of comport
* } EventObject;
*
* When an event occurs, the PSTN thread makes a call directly to the
* ComPort object to notify it. If the event that occured was a WRITE
* event, we also issue a PollTransmitter() function call to the
* g_pController object so that it can transmit more data.
*
* If a READ event occurs, we call the ComPort object directly to notify
* it that the event occurred. We then issue a PollReceiver() function
* call to the g_pController object so that we can process the
* received data. We then issue a PollTransmitter() to the
* g_pController object. Logically, this doesn't make much sense,
* but if you study the Q922 code, you'll see that we sometimes don't
* transmit data until we receive notification that previously transmitted
* data was successfully transmitted.
*
* If a CONTROL event occurs, we call the ComPort object directly to
* notify it that the event occurred. We then issue a PollReceiver()
* function call to the g_pController object so that the CONTROL
* event will be processed.
*
*
*
* X.214 INTERFACE
*
* You will notice that many of the entry points to this DLL were taken
* from the X.214 service definition. These entry points are consistent
* between all DataBeam Transport DLLs. This gives the user application
* a consistent interface.
*
*
* PHYSICAL API
*
* Some of the entry points to this DLL are specific to the creation
* and use of LOGICAL connections, and some of the entry points are
* specific to the creation and deletion of PHYSICAL connections. The
* out-of-band physical API is new as of version 2.0 and provides the
* user with an optional way of making PHYSICAL connections. In earlier
* versions of the Transport stacks, there was no Physical API and PHYSICAL
* connections were made in-band using the TConnectRequest() and
* TDisconnectRequest() calls. This form of call control is still
* supported in this version.
*
* Although the user can use out-of-band (PHYSICAL call control) and
* in-band call control, the DLL must be put in one mode or the other.
* The DLL can NOT function in both modes simultaneously. The user
* determines the DLL's mode of operation by the calls it makes to it.
* If the user makes the TPhysicalInitialize() function call to the DLL
* before it issues the TInitialize() function call, the DLL is in the
* out-of-band call control mode. If the user only makes the TInitialize()
* function call, by default the DLL will be in the in-band call control
* mode.
*
* If MCS will be using this DLL, it makes the TInitialize() function call.
* Therefore, if the user wants to use the out-of-band call control mode,
* it should call TPhysicalInitialize() before it initializes MCS.
*
*
* Static Variables:
* Static_Instance_Handle - Instance handle passed to the DLL when it
* is started. The instance handle is used
* for Win32 system calls.
* g_pController - This is a pointer to the controller that
* maintains the T123 stacks.
* m_cUsers - This is incremented each time someone
* calls the TInitialize function. It is
* decremented each time someone calls the
* TCleanup function. When this value
* reaches 0, we destroy the transport
* controller.
* g_hWorkerThread - This global variable keeps the handle of
* the thread we spawn on initialization.
* g_dwWorkerThreadID - Thread identifier
* g_hWorkerThreadEvent - This is an event object that is used to
* synchronize actions between the 2 threads.
* g_hThreadTerminateEvent - This event is used to signal the PSTN
* thread that it needs to terminate.
* ComPort_List - This is a list of the ComPort objects
* serviced by this DLL.
* class.
* Number_Of_Diagnostic_Users - This variable keeps up with the number of
* people using who called T120DiagnosticCreate.
* We won't delete the Diagnostic class until
* an equal number of people call
* T120DiagnosticDestroy.
*
*/
/*
** External Interfaces
*/
#include "tprtintf.h"
#include "comport.h"
#define TPRTINTF_MAXIMUM_WAIT_OBJECTS (1 + 16)
/*
** Global variables used within the C to C++ converter.
*/
HINSTANCE g_hDllInst = NULL;
CTransportInterface *g_pTransportInterface = NULL;
CRITICAL_SECTION g_csPSTN;
Timer *g_pSystemTimer = NULL;
TransportController *g_pController = NULL;
HANDLE g_hWorkerThread = NULL;
DWORD g_dwWorkerThreadID = 0;
HANDLE g_hWorkerThreadEvent= NULL;
HANDLE g_hThreadTerminateEvent = NULL;
DictionaryClass *g_pComPortList2 = NULL;
SListClass *g_pPSTNEventList = NULL;
TransportCallback g_pfnT120Notify = NULL;
void *g_pUserData = NULL;
BOOL g_fEventListChanged = FALSE;
/*
** The following defines the MCATPSTN User window class name.
** This name must be unique, system wide.
*/
#define MCATPSTN_USER_WINDOW_CLASS_NAME "NMPSTN USER Message Window"
/*
/*
** Timer duration. We will poll the DLL every X milliseconds. During
** this time, we can do any maintenance that is necessary
*/
#define MCATPSTN_TIMER_DURATION 250
/*
** These are prototypes
*/
DWORD T123_WorkerThreadProc(DWORD *);
/*
* BOOL WINAPI DllMain (
* HINSTANCE instance_handle,
* DWORD reason,
* LPVOID)
*
* Functional Description:
* This routine is the DLL startup routine. It is analagous to the
* constructor of a class. It is called by Windows when the DLL is
* loaded and when other significant events occur.
*/
#ifdef _DEBUG
#define INIT_DBG_ZONE_DATA
#include "fsdiag2.h"
#endif /* _DEBUG */
BOOL WINAPI DllMain(HINSTANCE hDllInst, DWORD reason, LPVOID)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
g_hDllInst = hDllInst;
::DisableThreadLibraryCalls(hDllInst);
#ifdef _DEBUG
MLZ_DbgInit((PSTR *) &c_apszDbgZones[0],
(sizeof(c_apszDbgZones) / sizeof(c_apszDbgZones[0])) - 1);
#endif
DBG_INIT_MEMORY_TRACKING(hDllInst);
::InitializeCriticalSection(&g_csPSTN);
break;
case DLL_PROCESS_DETACH:
::DeleteCriticalSection(&g_csPSTN);
DBG_CHECK_MEMORY_TRACKING(hDllInst);
#ifdef _DEBUG
MLZ_DbgDeInit();
#endif
break;
}
return TRUE;
}
TransportError WINAPI T123_CreateTransportInterface(ILegacyTransport **pp)
{
TransportError rc;
if (NULL != pp)
{
*pp = NULL;
if (NULL == g_pTransportInterface)
{
rc = TRANSPORT_MEMORY_FAILURE;
DBG_SAVE_FILE_LINE
g_pTransportInterface = new CTransportInterface(&rc);
if (NULL != g_pTransportInterface && TRANSPORT_NO_ERROR == rc)
{
*pp = (ILegacyTransport *) g_pTransportInterface;
return TRANSPORT_NO_ERROR;
}
delete g_pTransportInterface;
g_pTransportInterface = NULL;
return rc;
}
ASSERT(0);
return TRANSPORT_ALREADY_INITIALIZED;
}
return TRANSPORT_INVALID_PARAMETER;
}
CTransportInterface::CTransportInterface(TransportError *rc)
:
m_cUsers(0)
{
g_hWorkerThread = NULL;
g_dwWorkerThreadID = 0;
g_pfnT120Notify = NULL;
g_pUserData = NULL;
g_fEventListChanged = FALSE;
DBG_SAVE_FILE_LINE
g_pSystemTimer = new Timer;
ASSERT(NULL != g_pSystemTimer);
DBG_SAVE_FILE_LINE
g_hWorkerThreadEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
ASSERT(NULL != g_hWorkerThreadEvent);
DBG_SAVE_FILE_LINE
g_hThreadTerminateEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
ASSERT(NULL != g_hThreadTerminateEvent);
DBG_SAVE_FILE_LINE
g_pComPortList2 = new DictionaryClass(TRANSPORT_HASHING_BUCKETS);
ASSERT(NULL != g_pComPortList2);
DBG_SAVE_FILE_LINE
g_pPSTNEventList = new SListClass;
ASSERT(NULL != g_pPSTNEventList);
*rc = (g_pSystemTimer &&
g_hWorkerThreadEvent &&
g_hThreadTerminateEvent &&
g_pComPortList2 &&
g_pPSTNEventList)
?
TRANSPORT_NO_ERROR :
TRANSPORT_INITIALIZATION_FAILED;
ASSERT(TRANSPORT_NO_ERROR == *rc);
}
CTransportInterface::~CTransportInterface(void)
{
if (m_cUsers > 0)
{
// TCleanup() was not properly called enough times in the process.
// Force final cleanup.
m_cUsers = 1;
TCleanup();
}
delete g_pSystemTimer;
g_pSystemTimer = NULL;
delete g_pComPortList2;
g_pComPortList2 = NULL;
delete g_pPSTNEventList;
g_pPSTNEventList = NULL;
g_pfnT120Notify = NULL;
g_pUserData = NULL;
if (NULL != g_hWorkerThreadEvent)
{
::CloseHandle(g_hWorkerThreadEvent);
g_hWorkerThreadEvent = NULL;
}
if (NULL != g_hThreadTerminateEvent)
{
::CloseHandle(g_hThreadTerminateEvent);
g_hThreadTerminateEvent = NULL;
}
if (NULL != g_hWorkerThread)
{
::CloseHandle(g_hWorkerThread);
g_hWorkerThread = NULL;
}
}
void CTransportInterface::ReleaseInterface(void)
{
delete this;
}
/*
* TransportError APIENTRY TInitialize (
* TransportCallback transport_callback,
* PVoid user_defined)
*
* Functional Description:
* This function must be called by the user of the Transport
* stack. The PSTN thread is started.
*
* Caveats:
* If this function is successful, the user must also call the
* TCleanup() function when he is finished with the DLL.
*/
TransportError CTransportInterface::TInitialize
(
TransportCallback transport_callback,
void *user_defined
)
{
TransportError rc = TRANSPORT_NO_ERROR;
if (! m_cUsers)
{
g_pfnT120Notify = transport_callback;
g_pUserData = user_defined;
/*
** At this point, we need to create another thread that will
** maintain this DLL.
*/
g_hWorkerThread = ::CreateThread(
NULL, 0, (LPTHREAD_START_ROUTINE) T123_WorkerThreadProc,
NULL, 0, &g_dwWorkerThreadID);
if (NULL != g_hWorkerThread)
{
if (::SetThreadPriority(g_hWorkerThread, THREAD_PRIORITY_ABOVE_NORMAL) == FALSE)
{
WARNING_OUT(("SetThreadPriority ERROR: = %d", ::GetLastError()));
}
/*
** Wait for the secondary thread to notify us that initialization is
** complete
*/
::WaitForSingleObject(g_hWorkerThreadEvent, 30000); // 30 seconds
/*
** If g_pController == NULL, initialization FAILED. We use
** this variable to signal us regarding the status of the secondary
** thread.
*/
if (NULL == g_pController)
{
ERROR_OUT(("TInitialize: Unable to initialize transport"));
rc = TRANSPORT_INITIALIZATION_FAILED;
}
}
else
{
rc = TRANSPORT_INITIALIZATION_FAILED;
}
}
if (TRANSPORT_NO_ERROR == rc)
{
m_cUsers++;
}
return rc;
}
/*
* TransportError TCleanup (void)
*
* Functional Description:
* This function is called to release all system resources that
* were used during the life of the DLL.
*
* Caveats:
* This function should only be called if the user had previously
* called the TInitialize() function.
*/
TransportError CTransportInterface::TCleanup(void)
{
TransportError rc = TRANSPORT_NOT_INITIALIZED;
if (m_cUsers > 0)
{
if (--m_cUsers == 0)
{
/*
** Set the g_hThreadTerminateEvent to notify the PSTN thread
** that it should terminate operations.
*/
::SetEvent(g_hThreadTerminateEvent);
/*
** Wait for the PSTN thread to notify us that cleanup is
** complete
*/
if (g_hWorkerThreadEvent != NULL)
{
// WaitForSingleObject (g_hWorkerThreadEvent, INFINITE);
::WaitForSingleObject(g_hWorkerThreadEvent, 30000); // 30 seconds
g_hWorkerThreadEvent = NULL;
//
// Relinquish the remainder of our time slice, to allow trasnsport thread to exit.
//
Sleep(0);
}
}
rc = TRANSPORT_NO_ERROR;
}
return rc;
}
TransportError CTransportInterface::TCreateTransportStack
(
BOOL fCaller,
HANDLE hCommLink,
HANDLE hevtClose,
PLUGXPRT_PARAMETERS *pParams
)
{
TransportError rc = TRANSPORT_NOT_INITIALIZED;
if (m_cUsers > 0)
{
::EnterCriticalSection(&g_csPSTN);
if (NULL != g_pController)
{
rc = g_pController->CreateTransportStack(fCaller, hCommLink, hevtClose, pParams);
}
::LeaveCriticalSection(&g_csPSTN);
}
return rc;
}
TransportError CTransportInterface::TCloseTransportStack
(
HANDLE hCommLink
)
{
TransportError rc = TRANSPORT_NOT_INITIALIZED;
if (m_cUsers > 0)
{
::EnterCriticalSection(&g_csPSTN);
if (NULL != g_pController)
{
rc = g_pController->CloseTransportStack(hCommLink);
}
::LeaveCriticalSection(&g_csPSTN);
}
return rc;
}
/*
* TransportError APIENTRY TConnectRequest (
* TransportAddress transport_address,
* TransportPriority transport_priority,
* LogicalHandle *logical_handle)
*
* Functional Description:
* This function starts the process of building a transport connection.
* It uses the transport_address and attempts to make a connection to it.
*
* If the DLL is set up for in-band call control, the DLL will choose a
* modem and attempt a connection. After the physical connection is up,
* it will create a logical connection that the user application can use.
*
* If the DLL is set up for out-of-band call control, the transport address
* is actually an ascii string that represents a DLL generated
* physicalhandle. This call can only be made in response to a successful
* TPhysicalConnectRequest() function. The TPhysicalConnectRequest() call
* returns a PhysicalHandle that the user must convert to ascii and pass
* back to the DLL via this call. Although this is very ugly, we did this
* for a reason. We added the out-of-band call control API to the
* transports but we did not want to change GCC or MCS to do it. GCC and
* MCS already expected an ascii string as the transport address. In the
* future, look for this to change in MCS and GCC.
*/
TransportError CTransportInterface::TConnectRequest
(
LEGACY_HANDLE *logical_handle,
HANDLE hCommLink
)
{
TransportError rc = TRANSPORT_NOT_INITIALIZED;
if (m_cUsers > 0)
{
::EnterCriticalSection(&g_csPSTN);
if (NULL != g_pController)
{
rc = g_pController->ConnectRequest(logical_handle, hCommLink);
}
::LeaveCriticalSection(&g_csPSTN);
}
return rc;
}
/*
* TransportError APIENTRY TDisconnectRequest (
* LogicalHandle logical_handle,
* BOOL trash_packets)
*
* Functional Description:
* This function breaks a logical connection. This is also an X.214
* primitive. Since the DLL may have packets queued up for transmission
* for this logical connection, the trash_packets parameter determines
* whether the DLL should trash those packets before disconnecting the
* logical connection.
*/
TransportError CTransportInterface::TDisconnectRequest
(
LEGACY_HANDLE logical_handle,
BOOL trash_packets
)
{
TransportError rc = TRANSPORT_NOT_INITIALIZED;
if (m_cUsers > 0)
{
::EnterCriticalSection(&g_csPSTN);
if (NULL != g_pController)
{
rc = g_pController->DisconnectRequest(logical_handle, trash_packets);
}
::LeaveCriticalSection(&g_csPSTN);
}
return rc;
}
/*
* TransportError APIENTRY TDataRequest (
* LogicalHandle logical_handle,
* LPBYTE user_data,
* ULONG user_data_length)
*
* Functional Description:
* This function takes the data passed in and attempts to send it
* to the remote site.
*/
TransportError CTransportInterface::TDataRequest
(
LEGACY_HANDLE logical_handle,
LPBYTE user_data,
ULONG user_data_length
)
{
TransportError rc = TRANSPORT_NOT_INITIALIZED;
if (m_cUsers > 0)
{
::EnterCriticalSection(&g_csPSTN);
if (NULL != g_pController)
{
rc = g_pController->DataRequest(logical_handle, user_data, user_data_length);
/*
** If the packet was accepted, we need to issue a
** PollTransmitter() to flush the packet out. Otherwise,
** we will have a latency problem.
*/
if (TRANSPORT_NO_ERROR == rc)
{
ComPort *comport;
PhysicalHandle physical_handle = g_pController->GetPhysicalHandle(logical_handle);
if (g_pComPortList2->find((DWORD_PTR) physical_handle, (PDWORD_PTR) &comport))
{
if (! comport->IsWriteActive())
{
g_pController->PollTransmitter(physical_handle);
}
}
}
}
::LeaveCriticalSection(&g_csPSTN);
}
return rc;
}
/*
* TransportError APIENTRY TReceiveBufferAvailable (void)
*
* Functional Description:
* This function informs the transport to enable packet transfer from the
* Transport to the user application. This function makes a call to
* 'Enable' the receiver and then it issues a PollReceiver() to actually
* allow any pending packets to be sent on up to the user.
*/
TransportError CTransportInterface::TReceiveBufferAvailable(void)
{
TransportError rc = TRANSPORT_NOT_INITIALIZED;
if (m_cUsers > 0)
{
::EnterCriticalSection(&g_csPSTN);
if (NULL != g_pController)
{
g_pController->EnableReceiver();
g_pController->PollReceiver();
/*
** We are doing a PollTransmitter() here to allow
** the Q922 object to exit the 'receiver not ready' mode.
** If MCS had been rejecting packets, and now it is accepting
** packets, we need to let the remote site know that it
** can re-start its transmitter.
*/
g_pController->PollTransmitter();
}
::LeaveCriticalSection(&g_csPSTN);
rc = TRANSPORT_NO_ERROR;
}
return rc;
}
/*
* TransportError APIENTRY TPurgeRequest (
* LogicalHandle logical_handle)
*
* Functional Description:
* This function purges all of the outbound packets for the given
* logical connection.
*/
TransportError CTransportInterface::TPurgeRequest
(
LEGACY_HANDLE logical_handle
)
{
TransportError rc = TRANSPORT_NOT_INITIALIZED;
if (m_cUsers > 0)
{
::EnterCriticalSection(&g_csPSTN);
if (NULL != g_pController)
{
rc = g_pController->PurgeRequest(logical_handle);
}
::LeaveCriticalSection(&g_csPSTN);
}
return rc;
}
TransportError CTransportInterface::TEnableReceiver(void)
{
TransportError rc = TRANSPORT_NOT_INITIALIZED;
if (m_cUsers > 0)
{
::EnterCriticalSection(&g_csPSTN);
if (NULL != g_pController)
{
g_pController->EnableReceiver();
}
::LeaveCriticalSection(&g_csPSTN);
}
return TRANSPORT_NO_ERROR;
}
/*
* DWORD T123_WorkerThreadProc (LPDWORD)
*
* Functional Description:
* This function is the PSTN thread. It maintains the DLL.
*/
DWORD T123_WorkerThreadProc(DWORD *)
{
PEventObject event_struct;
BOOL fContinue = TRUE;
HANDLE aEvents[TPRTINTF_MAXIMUM_WAIT_OBJECTS];
ULONG cEvents;
ULONG last_time_polled;
ULONG current_time;
g_fEventListChanged = FALSE;
/*
** Create the one and only instance of the Transport Controller
** to be in charge of this DLL. Once it is created, all other
** primitives are routed to it.
*/
DBG_SAVE_FILE_LINE
g_pController = new TransportController();
if (g_pController == NULL)
{
ERROR_OUT(("PSTN_WorkerThreadProc: cannot allocate TransportController"));
::SetEvent(g_hWorkerThreadEvent);
return TRANSPORT_INITIALIZATION_FAILED;
}
/*
** The DLL needs to be polled every X milliseconds. We do this
** by checking the system clock and if X milliseconds have elapsed
** since the last DLL poll, we do it again. This line of code is
** actually initializing the timer.
*/
last_time_polled = ::GetTickCount();
/*
** Notify the primary thread that we are up
*/
::SetEvent(g_hWorkerThreadEvent);
// THREAD LOOP
/*
** This while loop is the heart of the PSTN thread. We use the
** WaitForMultipleObjects() function to wake up our thread when a
** significant event occurs OR when a timeout occurs.
**
** There are four different types of event objects that can occur
** that will wake up the thread.
**
** 1. The primary thread sets the g_hThreadTerminateEvent object.
** 2. Read event from the ComPort object. If a read of a comm port
** completes or times out, the event object is set
** 3. Write event from the ComPort object. If a write on a comm
** port completes or times out.
** 4. Control event from the ComPort object. If the carrier detect
** signal changes.
**
** Also, the thread will continue running if the MCATPSTN_TIMER_DURATION
** expires
**
**
** A ComPort object can add event objects to our PSTN Event_List at any
** time. When they are added to the Event_List, we add them to the
** local aEvents and the WaitForMultipleObjects() function waits
** for them.
**
** This approach works very well except that the WaitForMultipleObjects()
** function can only handle TPRTINTF_MAXIMUM_WAIT_OBJECTS at a time. Currently,
** this #define is set to 64 by the Windows 95 operating system. Since
** each ComPort uses 3 event objects, this limits us to a maximum of
** 21 active ports.
*/
aEvents[0] = g_hThreadTerminateEvent;
cEvents = 1;
while (fContinue)
{
/*
** Wait for an event to occur or for the timeout to expire
*/
DWORD dwRet = ::WaitForMultipleObjects(cEvents, aEvents, FALSE, MCATPSTN_TIMER_DURATION);
/*
** We MUST enter this critical section of code and lock out the
** primary thread from enterring it. Both threads executing this
** code could be disasterous
*/
::EnterCriticalSection(&g_csPSTN);
if (dwRet == WAIT_FAILED)
{
fContinue = FALSE;
break;
}
else
if (dwRet == WAIT_TIMEOUT)
{
if (cEvents > 1)
{
last_time_polled = ::GetTickCount();
g_pController->PollReceiver();
g_pController->PollTransmitter();
g_pSystemTimer->ProcessTimerEvents();
}
}
else
{
/*
** Determine which object has signalled us
*/
ULONG dwIdx = dwRet - WAIT_OBJECT_0;
if (dwIdx > cEvents)
{
ERROR_OUT(("ThreadFunc: Invalid object signalled = %d", dwIdx));
}
else
{
/*
** If the object that signalled us is index 0, it is the
** terminate thread object.
*/
if (! dwIdx)
{
TRACE_OUT(("ThreadFunc: Terminating thread"));
fContinue = FALSE;
}
else
{
dwIdx--;
event_struct = (PEventObject) g_pPSTNEventList->read((USHORT) dwIdx);
if (NULL != event_struct)
{
/*
** Check the delete_event, if it is set, we need to
** terminate the event.
**
** By design, other objects throughout the DLL create
** the events, and this function deletes them. If the
** creating function deleted them, we could be waiting
** on an event that gets deleted. That has the potential
** of being messy.
*/
if (event_struct->delete_event)
{
/*
** Remove the ComPort from our list
*/
ComPort *comport = NULL;
if (g_pComPortList2->find((DWORD_PTR) event_struct->hCommLink, (PDWORD_PTR) &comport))
{
g_pComPortList2->remove((DWORD_PTR) event_struct->hCommLink);
comport->Release();
}
/*
** Close the event
*/
::CloseHandle(event_struct->event);
/*
** Remove the event from our list
*/
g_pPSTNEventList->remove((DWORD_PTR) event_struct);
delete event_struct;
g_fEventListChanged = TRUE;
}
else
{
ComPort *comport = event_struct->comport;
/*
** Switch on the event_type to determine the
** operation that needs to take place
*/
switch (event_struct->event_type)
{
case READ_EVENT:
comport->ProcessReadEvent();
/*
** If a READ event occurred, we need to issue
** a PollReceiver() and then issue a
** PollTransmitter(). We issue the
** PollTransmitter() because the PollReceiver()
** may have freed up space for us to send out
** data.
*/
g_pController->PollReceiver(event_struct->hCommLink);
g_pController->PollTransmitter(event_struct->hCommLink);
break;
case WRITE_EVENT:
/*
** If a WRITE_EVENT occurs, this means that
** space has become available to transmit more
** data.
*/
comport->ProcessWriteEvent();
g_pController->PollTransmitter(event_struct->hCommLink);
break;
case CONTROL_EVENT:
/*
** The CONTROL events are particular to the
** ComPort object. A change in the CD signal
** could be a CONTROL event.
*/
g_pController->PollReceiver(event_struct->hCommLink);
break;
default:
ERROR_OUT(("ThreadFunc: Illegal event type = %d", event_struct->event_type));
break;
} // switch
}
} // if event_struct
}
}
/*
** Check to see if enough time has elapsed since the last time we
** polled the DLL to do it again.
*/
if (cEvents > 1)
{
current_time = ::GetTickCount();
if ((current_time - last_time_polled) >= MCATPSTN_TIMER_DURATION)
{
last_time_polled = current_time;
g_pController->PollReceiver();
g_pController->PollTransmitter();
g_pSystemTimer->ProcessTimerEvents();
}
}
}
/*
** This Event_List_Changed will only be set to TRUE if we need to
** update our event list
*/
if (g_fEventListChanged)
{
aEvents[0] = g_hThreadTerminateEvent;
cEvents = 1;
if (g_pPSTNEventList->entries() > (TPRTINTF_MAXIMUM_WAIT_OBJECTS - 1))
{
ERROR_OUT(("ThreadFunc: ERROR: Number of event objects = %d",
g_pPSTNEventList->entries() + 1));
}
/*
** Go thru the Event_List and create a new aEvents.
*/
g_pPSTNEventList->reset();
while (g_pPSTNEventList->iterate((PDWORD_PTR) &event_struct))
{
aEvents[cEvents] = event_struct -> event;
cEvents++;
/*
** Add the physical_handle to the ComPort_List
*/
if (event_struct->event_type == WRITE_EVENT)
{
if (! g_pComPortList2->find((DWORD_PTR) event_struct->hCommLink))
{
g_pComPortList2->insert((DWORD_PTR) event_struct->hCommLink,
(DWORD_PTR) event_struct->comport);
}
}
}
g_fEventListChanged = FALSE;
}
::LeaveCriticalSection(&g_csPSTN);
} // while
// CLEANUP THE THREAD RESOURCES
delete g_pController;
g_pController = NULL;
/*
** Delete all of the event objects
*/
g_pPSTNEventList->reset();
while (g_pPSTNEventList->iterate((PDWORD_PTR) &event_struct))
{
::CloseHandle(event_struct->event);
delete event_struct;
}
/*
** Notify the primary thread that we are up
*/
::SetEvent(g_hWorkerThreadEvent);
return (0);
}
ULONG NotifyT120(ULONG msg, void *param)
{
if (NULL != g_pfnT120Notify)
{
return (*g_pfnT120Notify) (msg, param, g_pUserData);
}
return 0;
}
IObject::~IObject(void) { }