1250 lines
28 KiB
C++
1250 lines
28 KiB
C++
//+------------------------------------------------------------------------
|
||
//
|
||
// Microsoft Windows
|
||
// Copyright (C) Microsoft Corporation, 1993.
|
||
//
|
||
// File: procswap.cxx
|
||
//
|
||
// Contents: Program for measuring task switching performance
|
||
// between two windows programs. The program creates
|
||
// CTaskSwitch objects...
|
||
//
|
||
// Each Object can wait on one of three things...
|
||
// 1. GetMessage
|
||
// 2. MsgWaitForMultipleObjects
|
||
// 3. WaitForSingleObject (event)
|
||
//
|
||
// and when awoken, will signal another Object in one
|
||
// of three ways...
|
||
// 1. PostMessage
|
||
// 2. SendMessage
|
||
// 3. SetEvent
|
||
//
|
||
// These cases can be combined in any manner to obtain
|
||
// a maxtrix of possible scenarios.
|
||
//
|
||
// The CTaskSwitch objects can be in the same process on
|
||
// different threads, or in different processes.
|
||
//
|
||
// Classes: CEvent - event handling class
|
||
// CTaskSwitch - main task switch class
|
||
//
|
||
//
|
||
// Functions: WinMain - entry point of process
|
||
// ThreadEntry - entry point of spawned threads
|
||
// ThreadWndProc - processes windows messages
|
||
//
|
||
//
|
||
// History: 08-Feb-94 Rickhi Created
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
#include <benchmrk.hxx>
|
||
#include <tchar.h>
|
||
|
||
|
||
// execution parameter structure
|
||
|
||
typedef struct tagSExecParms
|
||
{
|
||
int oloop; // outer loop count
|
||
int iloop; // inner loop count
|
||
HWND hWndOther; // HWND of other process
|
||
HANDLE hEventOther; // Event Handle of other process
|
||
WNDPROC pfnWndProc; // ptr to WndProc function
|
||
TCHAR szFile[20]; // output file name
|
||
TCHAR szWaitEvent[20]; // event name to wait on
|
||
TCHAR szSignalEvent[20]; // event name to signal
|
||
} SExecParms;
|
||
|
||
|
||
typedef enum tagWAITTYPES
|
||
{
|
||
WAIT_EVENT = 1,
|
||
WAIT_MSGWAITFORMULTIPLE = 2,
|
||
WAIT_GETMESSAGE = 3,
|
||
WAIT_SYNCHRONOUS = 4
|
||
} WAITTYPES;
|
||
|
||
typedef enum tagSIGNALTYPES
|
||
{
|
||
SIGNAL_EVENT = 1,
|
||
SIGNAL_POSTMESSAGE = 2,
|
||
SIGNAL_SENDMESSAGE = 3,
|
||
SIGNAL_SYNCHRONOUS = 4
|
||
} SIGNALTYPES;
|
||
|
||
// input names corresponding to the wait types
|
||
LPSTR aszWait[] = {"", "event", "msgwait", "getmsg", "sync", NULL};
|
||
|
||
// input names corresponding to the signal types
|
||
LPSTR aszSignal[] = {"", "event", "postmsg", "sendmsg", "sync", NULL};
|
||
|
||
|
||
|
||
// Name of window class for dispatching messages.
|
||
|
||
#define MY_WINDOW_CLASS TEXT("ProcSwapWindowClass")
|
||
|
||
#define MAX_OLOOP 100
|
||
|
||
|
||
// globals
|
||
|
||
DWORD g_fFullInfo = 0; // write full info or not
|
||
HINSTANCE g_hInst = NULL; // misc windows junk.
|
||
ATOM g_MyClass = 0;
|
||
UINT g_MyMessage = WM_USER;
|
||
|
||
|
||
// function prototype
|
||
LRESULT ThreadWndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||
DWORD ThreadEntry(void *param);
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// Class: CEvent
|
||
//
|
||
// Purpose: class for blocking & starting threads.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
class CEvent
|
||
{
|
||
public:
|
||
CEvent(LPTSTR szName, HRESULT &hr) { Init(szName, hr); }
|
||
CEvent(void) {m_hdl = NULL; }
|
||
|
||
~CEvent() { CloseHandle(m_hdl); }
|
||
|
||
void Signal(void) { SetEvent(m_hdl); }
|
||
void Reset(void) { ; } // ResetEvent(m_hdl); }
|
||
void BlockS(void) { WaitForSingleObject(m_hdl, 60000); }
|
||
void BlockM(void) { WaitForMultipleObjects(1, &m_hdl, FALSE, 60000); }
|
||
HANDLE *GetHdl(void) { return &m_hdl; };
|
||
|
||
void Init(LPTSTR szName, HRESULT &hr);
|
||
|
||
private:
|
||
|
||
HANDLE m_hdl;
|
||
};
|
||
|
||
|
||
void CEvent::Init(LPTSTR szName, HRESULT &hr)
|
||
{
|
||
hr = S_OK;
|
||
|
||
// first try opening the event
|
||
m_hdl = OpenEvent(EVENT_ALL_ACCESS,
|
||
FALSE,
|
||
szName);
|
||
|
||
if (m_hdl == NULL)
|
||
{
|
||
// doesnt exist yet so create it.
|
||
m_hdl = CreateEvent(NULL, // security
|
||
FALSE, // auto reset
|
||
FALSE, // initially not signalled
|
||
szName);
|
||
|
||
if (m_hdl == NULL)
|
||
{
|
||
_tprintf (TEXT("Error Creating CEvent (%s)\n"), szName);
|
||
hr = GetLastError();
|
||
}
|
||
else
|
||
{
|
||
_tprintf (TEXT("Created CEvent (%s)\n"), szName);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
_tprintf (TEXT("Opened CEvent (%s)\n"), szName);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// Class: CTaskSwitch
|
||
//
|
||
// Purpose: class for timing task switches.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
class CTaskSwitch
|
||
{
|
||
public:
|
||
CTaskSwitch(LPSTR lpszCmdLine, HRESULT &hr);
|
||
~CTaskSwitch(void);
|
||
|
||
int MainProcessLoop(void);
|
||
LRESULT ThreadWndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||
HRESULT SpawnOtherSide(void);
|
||
|
||
private:
|
||
|
||
// initialization / cleanup methods
|
||
|
||
HRESULT ParseCmdLine(LPSTR lpszCmdLine, SExecParms &execp);
|
||
HRESULT WindowInitialize(WNDPROC pfnWndProc, HWND &hWnd);
|
||
void WindowUninitialize(HWND hWnd);
|
||
void CreateOtherParms(void);
|
||
void WriteExecParms(void);
|
||
void WriteResults(void);
|
||
void Help(void);
|
||
DWORD GetWaitType(LPSTR pszCmd);
|
||
DWORD GetSignalType(LPSTR pszCmd);
|
||
DWORD CreateProc(void);
|
||
|
||
// processing methods
|
||
|
||
HRESULT SendOrWaitFirstSignal(void);
|
||
void ProcessMsgWaitForMultiple(DWORD dwRet);
|
||
void ProcessIncommingEvent(void);
|
||
void UpdateLoopCounters(void);
|
||
void SignalOtherSide(void);
|
||
|
||
// data
|
||
|
||
BOOL g_fDone; // when to exit the loop
|
||
BOOL g_fKicker; // we kick the other guy
|
||
BOOL g_fThreadSwitch; // thread or process switching?
|
||
BOOL g_fWaitMultiple; // wait single or multiple
|
||
|
||
ULONG g_oloop; // outer loop counter
|
||
ULONG g_iloop; // inner loop counter
|
||
|
||
DWORD g_WaitType; // what to wait on
|
||
DWORD g_SignalType; // what to signal
|
||
|
||
// used only for parameter parseing
|
||
DWORD g_WaitType1; // what to wait on
|
||
DWORD g_SignalType1; // what to signal
|
||
DWORD g_WaitType2; // what to wait on
|
||
DWORD g_SignalType2; // what to signal
|
||
|
||
|
||
HWND g_hWndOther; // hWnd of other side
|
||
HWND g_hWndMe; // my hWnd
|
||
|
||
CEvent g_WaitEvent; // event to wait on
|
||
CEvent g_SignalEvent; // event to signal
|
||
|
||
ULONG g_time[MAX_OLOOP]; // place to store the timings.
|
||
CStopWatch g_timer; // global timer proc 1
|
||
|
||
SExecParms g_execp; // execution parameters
|
||
CTestOutput * g_output; // output log
|
||
|
||
HRESULT g_hr; // result code
|
||
|
||
CHAR g_szOtherParms[MAX_PATH]; // parm string for other guy
|
||
};
|
||
|
||
|
||
|
||
|
||
// task switch objects - must be global for ThreadWndProc
|
||
|
||
CTaskSwitch *g_pTaskSwitch1 = NULL;
|
||
CTaskSwitch *g_pTaskSwitch2 = NULL;
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// WinMain - main entry point of program. May just call ThreadEntry, or
|
||
// may spawn another thread in the case of thread switching.
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
int WinMain(HINSTANCE hinst, HINSTANCE hPrev, LPSTR lpszCmdLine, int CmdShow)
|
||
{
|
||
HRESULT hr;
|
||
|
||
// create the first task switch object for this process
|
||
g_pTaskSwitch1 = new CTaskSwitch(lpszCmdLine, hr);
|
||
|
||
if (hr == S_OK)
|
||
{
|
||
// spawn a new thread or a new process to do task switching with.
|
||
hr = g_pTaskSwitch1->SpawnOtherSide();
|
||
|
||
if (hr == S_OK)
|
||
{
|
||
// enter the main processing loop
|
||
g_pTaskSwitch1->MainProcessLoop();
|
||
}
|
||
}
|
||
|
||
// print the results
|
||
delete g_pTaskSwitch1;
|
||
return 1;
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// ThreadEntry - main entry point for a thread spawned by CreateThread
|
||
// in the case of task switching between threads.
|
||
//
|
||
// Creates an instance of the CTaskSwitch class and invokes it
|
||
// main function.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
DWORD ThreadEntry(void *param)
|
||
{
|
||
LPSTR lpszCmdLine = (LPSTR) param;
|
||
|
||
HRESULT hr;
|
||
|
||
// create the second task switch object for this process
|
||
g_pTaskSwitch2 = new CTaskSwitch(lpszCmdLine, hr);
|
||
|
||
if (hr == S_OK)
|
||
{
|
||
// enter the main processing loop
|
||
g_pTaskSwitch2->MainProcessLoop();
|
||
}
|
||
|
||
// print the results
|
||
delete g_pTaskSwitch2;
|
||
return hr;
|
||
}
|
||
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// Dipatch to the correct CTaskSwitch object if the message is our
|
||
// special message.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
LRESULT ThreadWndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||
{
|
||
if (msg == g_MyMessage)
|
||
{
|
||
// its my special message, go handle it.
|
||
// here i have to select which object to dispatch to for the
|
||
// multithreaded case. i base that decision on lparam.
|
||
|
||
if (lparam == 0)
|
||
{
|
||
// use the first task switch object
|
||
return g_pTaskSwitch1->ThreadWndProc(hWnd, msg, wparam, lparam);
|
||
}
|
||
else
|
||
{
|
||
// use the second task switch object
|
||
return g_pTaskSwitch2->ThreadWndProc(hWnd, msg, wparam, lparam);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// let the default window procedure have the message.
|
||
return DefWindowProc(hWnd, msg, wparam, lparam);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// Constructor : parse the command line, create the events, create the
|
||
// window, and open a log file.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
CTaskSwitch::CTaskSwitch(LPSTR lpszCmdLine, HRESULT &hr) :
|
||
g_fDone(FALSE),
|
||
g_fKicker(FALSE),
|
||
g_fThreadSwitch(FALSE),
|
||
g_fWaitMultiple(FALSE),
|
||
g_oloop(10),
|
||
g_iloop(100),
|
||
g_WaitType(WAIT_EVENT),
|
||
g_SignalType(SIGNAL_EVENT),
|
||
g_hWndMe(NULL),
|
||
g_hWndOther(NULL),
|
||
g_output(NULL),
|
||
g_hr(S_OK)
|
||
{
|
||
// parse command line and write the parms to log file.
|
||
g_hr = ParseCmdLine(lpszCmdLine, g_execp);
|
||
|
||
if (g_hr == S_OK)
|
||
{
|
||
// Create a log file & write execution parameters
|
||
g_output = new CTestOutput(g_execp.szFile);
|
||
WriteExecParms();
|
||
|
||
// create the window for this thread
|
||
g_hr = WindowInitialize(g_execp.pfnWndProc, g_hWndMe);
|
||
if (g_hr == S_OK)
|
||
{
|
||
// Create the Wait event.
|
||
g_WaitEvent.Init(g_execp.szWaitEvent, g_hr);
|
||
if (g_hr == S_OK)
|
||
{
|
||
// Create the Signal event.
|
||
g_SignalEvent.Init(g_execp.szSignalEvent, g_hr);
|
||
|
||
if (g_hr == S_OK)
|
||
{
|
||
// create paramters to send to other side.
|
||
CreateOtherParms();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// return the results
|
||
hr = g_hr;
|
||
}
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// Desructor
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
CTaskSwitch::~CTaskSwitch(void)
|
||
{
|
||
// write the results
|
||
WriteResults();
|
||
|
||
// cleanup window registration
|
||
WindowUninitialize(g_hWndMe);
|
||
|
||
// close the log file
|
||
delete g_output;
|
||
}
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// Spawns either another process or another thread to perform the task
|
||
// switching with.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
HRESULT CTaskSwitch::SpawnOtherSide(void)
|
||
{
|
||
if (g_fKicker)
|
||
{
|
||
// i'm already the second entry, dont spawn anything.
|
||
// sleep for a bit to make sure both sides are ready
|
||
// when i kick things off.
|
||
|
||
Sleep(1000);
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
if (g_fThreadSwitch)
|
||
{
|
||
// spawn a thread
|
||
|
||
HANDLE hdl;
|
||
DWORD dwId;
|
||
hdl = CreateThread(NULL, // default security
|
||
0, // default stack size
|
||
ThreadEntry, // entry point
|
||
g_szOtherParms, // command line parms
|
||
0, // flags
|
||
&dwId); // threadid
|
||
|
||
if (hdl)
|
||
{
|
||
// dont need the handle
|
||
CloseHandle(hdl);
|
||
}
|
||
else
|
||
{
|
||
// what went wrong?
|
||
return GetLastError();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// spawn a process
|
||
DWORD dwRet = CreateProc();
|
||
if (dwRet != S_OK)
|
||
{
|
||
return dwRet;
|
||
}
|
||
}
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// MainProcessLoop - does the main wait & process the event
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
int CTaskSwitch::MainProcessLoop(void)
|
||
{
|
||
MSG msg;
|
||
DWORD dwRet;
|
||
|
||
|
||
// Send, or wait on, the first signal.
|
||
SendOrWaitFirstSignal();
|
||
|
||
|
||
// Reset the timer and enter the main loop.
|
||
g_timer.Reset();
|
||
|
||
|
||
// wait loop - based on the type of event we should receive, we
|
||
// wait here until such an event occurs. Then we send a signal
|
||
// to the other side based on what it expects from us.
|
||
|
||
while (!g_fDone)
|
||
{
|
||
switch (g_WaitType)
|
||
{
|
||
|
||
case WAIT_MSGWAITFORMULTIPLE:
|
||
|
||
// wait here for a message or an event to be signalled
|
||
dwRet = MsgWaitForMultipleObjects(1,
|
||
g_WaitEvent.GetHdl(),
|
||
FALSE,
|
||
600000,
|
||
QS_ALLINPUT);
|
||
|
||
// Dispatch to ThreadWndProc if a message, or
|
||
// to ProcessEvent if an event was signalled
|
||
ProcessMsgWaitForMultiple(dwRet);
|
||
break;
|
||
|
||
|
||
case WAIT_GETMESSAGE:
|
||
|
||
// wait for a windows message
|
||
if (GetMessage(&msg, NULL, 0, 0))
|
||
{
|
||
// dispatches to my ThreadWndProc
|
||
DispatchMessage(&msg);
|
||
}
|
||
break;
|
||
|
||
|
||
case WAIT_EVENT:
|
||
|
||
// wait for the event to be signalled
|
||
if (g_fWaitMultiple)
|
||
g_WaitEvent.BlockM();
|
||
else
|
||
g_WaitEvent.BlockS();
|
||
|
||
// process the event
|
||
ProcessIncommingEvent();
|
||
break;
|
||
|
||
|
||
case WAIT_SYNCHRONOUS:
|
||
|
||
// we have a synchronous singal to the other side, so there is
|
||
// nothing to wait on, we will just go make another synchronous
|
||
// call. this is valid only if the g_SignalType is
|
||
// SIGNAL_SENDMESSAGE.
|
||
|
||
ProcessIncommingEvent();
|
||
break;
|
||
|
||
|
||
default:
|
||
|
||
// unknown event
|
||
break;
|
||
}
|
||
|
||
} // while
|
||
|
||
return msg.wParam; // Return value from PostQuitMessage
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// processes a wakeup from MsgWaitForMultiple. Determines if the event was
|
||
// a message arrival (in which case it Peeks it and Dispatches it, or if it
|
||
// was an event signalled, in which case it calls the event handler.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
void CTaskSwitch::ProcessMsgWaitForMultiple(DWORD dwRet)
|
||
{
|
||
MSG msg;
|
||
|
||
if (dwRet == WAIT_OBJECT_0)
|
||
{
|
||
// our event got signalled, update the counters
|
||
ProcessIncommingEvent();
|
||
}
|
||
|
||
else if (dwRet == WAIT_OBJECT_0 + 1)
|
||
{
|
||
// some windows message was received. dispatch it.
|
||
|
||
if (PeekMessage(&msg, g_hWndMe, 0, 0, PM_REMOVE))
|
||
{
|
||
DispatchMessage(&msg);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// our event timed out or our event was abandoned or
|
||
// an error occurred.
|
||
|
||
g_fDone = TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// processes an incomming event. Just updates the counters and
|
||
// signals the other side.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
void CTaskSwitch::ProcessIncommingEvent(void)
|
||
{
|
||
// update the loop counters
|
||
UpdateLoopCounters();
|
||
|
||
// Signal the other side
|
||
SignalOtherSide();
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// process the incomming message
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
LRESULT CTaskSwitch::ThreadWndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||
{
|
||
// save the callers hWnd
|
||
g_hWndOther = (HWND) wparam;
|
||
|
||
// process as usual
|
||
ProcessIncommingEvent();
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// updates the global loop counters, reseting the time when the inner
|
||
// loop counter expires, and setting the fDone when the outer and inner
|
||
// loop counters expire.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
void CTaskSwitch::UpdateLoopCounters(void)
|
||
{
|
||
if (g_iloop == 0)
|
||
{
|
||
// get time for latest outer loop
|
||
g_time[g_oloop] = g_timer.Read();
|
||
|
||
if (g_oloop == 0)
|
||
{
|
||
// that was the last outerloop, we're done.
|
||
g_fDone = TRUE;
|
||
}
|
||
else
|
||
{
|
||
// update the counters
|
||
g_iloop = g_execp.iloop;
|
||
--g_oloop;
|
||
|
||
// restart the timer
|
||
g_timer.Reset();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// just update the inner loop count
|
||
--g_iloop;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// signals the other process or thread according to the SendType (either
|
||
// signals an event or posts a message).
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
void CTaskSwitch::SignalOtherSide(void)
|
||
{
|
||
switch (g_SignalType)
|
||
{
|
||
|
||
case SIGNAL_EVENT:
|
||
|
||
// signal the other sides event
|
||
g_SignalEvent.Signal();
|
||
break;
|
||
|
||
|
||
case SIGNAL_POSTMESSAGE:
|
||
|
||
// post a message to the other sides window handle.
|
||
// lparam tells ThreadWndProc which object to dispatch to, either
|
||
// g_pTaskSwitch1 or g_pTaskSwitch2. We only go to 2 if we are
|
||
// doing thread switches AND the poster is not the kicker.
|
||
|
||
PostMessage(g_hWndOther,
|
||
g_MyMessage,
|
||
(WPARAM)g_hWndMe,
|
||
(g_fThreadSwitch && !g_fKicker));
|
||
break;
|
||
|
||
|
||
case SIGNAL_SENDMESSAGE:
|
||
|
||
// send a message to the other side. this is a synchronous
|
||
// event. see comment in PostMessage above regarding lparam.
|
||
|
||
SendMessage(g_hWndOther,
|
||
g_MyMessage,
|
||
(WPARAM)g_hWndMe,
|
||
(g_fThreadSwitch && !g_fKicker));
|
||
break;
|
||
|
||
|
||
case SIGNAL_SYNCHRONOUS:
|
||
|
||
// the event we received is a synchronous event. there is no need
|
||
// to do anything to wake the other side.
|
||
|
||
break;
|
||
|
||
|
||
default:
|
||
|
||
// unknown signal type
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// signals the other process or thread that it can begin the test. this
|
||
// avoids timings skewed due to process startup latency.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
HRESULT CTaskSwitch::SendOrWaitFirstSignal(void)
|
||
{
|
||
if (g_fKicker)
|
||
{
|
||
// send a signal to drop the otherside into its wait loop, and
|
||
// then call SignalOtherSide to start the cycle, kicking the
|
||
// other side out of his first wait.
|
||
|
||
printf ("Initial Signal to Other Side\n");
|
||
g_SignalEvent.Signal();
|
||
SignalOtherSide();
|
||
}
|
||
else
|
||
{
|
||
printf ("Waiting for Signal From Other Side\n");
|
||
g_WaitEvent.BlockS();
|
||
}
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// initializes the window with the specified window proc.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
HRESULT CTaskSwitch::WindowInitialize(WNDPROC pfnWndProc, HWND &hWnd)
|
||
{
|
||
|
||
if (!g_MyMessage)
|
||
{
|
||
// Register my message type
|
||
g_MyMessage = RegisterWindowMessage(
|
||
TEXT("Component Object Model Remote Request Arrival") );
|
||
}
|
||
|
||
if (!g_MyClass)
|
||
{
|
||
// Register my window class.
|
||
WNDCLASS wcls;
|
||
|
||
wcls.style = 0;
|
||
wcls.lpfnWndProc = pfnWndProc;
|
||
wcls.cbClsExtra = 0;
|
||
wcls.cbWndExtra = 0;
|
||
wcls.hInstance = g_hInst;
|
||
wcls.hIcon = NULL;
|
||
wcls.hCursor = NULL;
|
||
wcls.hbrBackground = (HBRUSH) COLOR_BACKGROUND + 1;
|
||
wcls.lpszMenuName = NULL;
|
||
wcls.lpszClassName = MY_WINDOW_CLASS;
|
||
|
||
g_MyClass = RegisterClass( &wcls );
|
||
}
|
||
|
||
|
||
if (g_MyClass)
|
||
{
|
||
// Create a hidden window.
|
||
hWnd = CreateWindowEx( 0,
|
||
(LPCTSTR) g_MyClass,
|
||
TEXT("Task Switcher"),
|
||
WS_DISABLED,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
NULL,
|
||
NULL,
|
||
g_hInst,
|
||
NULL );
|
||
|
||
|
||
if (hWnd)
|
||
{
|
||
printf ("Created Window with hWnd %x\n", hWnd);
|
||
return S_OK;
|
||
}
|
||
}
|
||
|
||
return E_OUTOFMEMORY;
|
||
}
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// destroys the window and unregisters the class.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
void CTaskSwitch::WindowUninitialize(HWND hWnd)
|
||
{
|
||
if (hWnd != NULL)
|
||
{
|
||
DestroyWindow(hWnd);
|
||
}
|
||
|
||
if (g_MyClass != 0)
|
||
{
|
||
UnregisterClass(MY_WINDOW_CLASS, g_hInst);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// parses the command line and returns the execution parameters
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
HRESULT CTaskSwitch::ParseCmdLine(LPSTR lpszCmdLine, SExecParms &execp)
|
||
{
|
||
BOOL fFile = FALSE;
|
||
|
||
// set the default values for execution parameters.
|
||
|
||
execp.oloop = 10;
|
||
execp.iloop = 100;
|
||
execp.hWndOther = NULL;
|
||
execp.pfnWndProc = ::ThreadWndProc;
|
||
|
||
|
||
|
||
// check the input parameters
|
||
|
||
LPSTR pszCmd = lpszCmdLine;
|
||
LPSTR pszCmdNext = NULL;
|
||
|
||
while (pszCmd)
|
||
{
|
||
pszCmdNext = strchr(pszCmd, ' ');
|
||
if (pszCmdNext)
|
||
{
|
||
*pszCmdNext = '\0';
|
||
pszCmdNext++;
|
||
}
|
||
|
||
// check for outer loop count
|
||
if (!_strnicmp(pszCmd, "/o:", 3))
|
||
{
|
||
execp.oloop = atoi (pszCmd+3);
|
||
if (execp.oloop > MAX_OLOOP)
|
||
execp.oloop = MAX_OLOOP;
|
||
}
|
||
|
||
// check for inner loop count
|
||
else if (!_strnicmp(pszCmd, "/i:", 3))
|
||
{
|
||
execp.iloop = atoi (pszCmd+3);
|
||
if (execp.iloop < 1)
|
||
execp.iloop = 1;
|
||
}
|
||
|
||
// check for window handle
|
||
else if (!_strnicmp(pszCmd, "/hwnd:", 6))
|
||
{
|
||
execp.hWndOther = (HWND) atoi (pszCmd+6);
|
||
}
|
||
|
||
// check for waiter or Kicker
|
||
else if (!_strnicmp(pszCmd, "/k", 2))
|
||
{
|
||
g_fKicker = TRUE;
|
||
}
|
||
|
||
// check for thread or process switch
|
||
else if (!_strnicmp(pszCmd, "/p", 2))
|
||
{
|
||
g_fThreadSwitch = FALSE;
|
||
}
|
||
|
||
// check for thread or process switch
|
||
else if (!_strnicmp(pszCmd, "/t", 2))
|
||
{
|
||
g_fThreadSwitch = TRUE;
|
||
}
|
||
|
||
// check for wait single or multiple
|
||
else if (!_strnicmp(pszCmd, "/m", 2))
|
||
{
|
||
g_fWaitMultiple = TRUE;
|
||
}
|
||
|
||
// check for wait event name
|
||
else if (!_strnicmp(pszCmd, "/e1:", 4))
|
||
{
|
||
#ifdef UNICODE
|
||
mbstowcs(execp.szWaitEvent, pszCmd+4, strlen(pszCmd+4)+1);
|
||
#else
|
||
strcpy(execp.szWaitEvent, pszCmd+4);
|
||
#endif
|
||
}
|
||
|
||
// check for signal event name
|
||
else if (!_strnicmp(pszCmd, "/e2:", 4))
|
||
{
|
||
#ifdef UNICODE
|
||
mbstowcs(execp.szSignalEvent, pszCmd+4, strlen(pszCmd+4)+1);
|
||
#else
|
||
strcpy(execp.szSignalEvent, pszCmd+4);
|
||
#endif
|
||
}
|
||
|
||
// check for output file name
|
||
else if (!_strnicmp(pszCmd, "/f:", 3))
|
||
{
|
||
fFile = TRUE;
|
||
#ifdef UNICODE
|
||
mbstowcs(execp.szFile, pszCmd+3, strlen(pszCmd+3)+1);
|
||
#else
|
||
strcpy(execp.szFile, pszCmd+3);
|
||
#endif
|
||
}
|
||
|
||
// check for wait type
|
||
else if (!_strnicmp(pszCmd, "/w1:", 4))
|
||
{
|
||
g_WaitType1 = GetWaitType(pszCmd+4);
|
||
}
|
||
else if (!_strnicmp(pszCmd, "/w2:", 4))
|
||
{
|
||
g_WaitType2 = GetWaitType(pszCmd+4);
|
||
}
|
||
|
||
// check for signal type
|
||
else if (!_strnicmp(pszCmd, "/s1:", 4))
|
||
{
|
||
g_SignalType1 = GetSignalType(pszCmd+4);
|
||
}
|
||
else if (!_strnicmp(pszCmd, "/s2:", 4))
|
||
{
|
||
g_SignalType2 = GetSignalType(pszCmd+4);
|
||
}
|
||
|
||
// check for help request
|
||
else if ((!_strnicmp(pszCmd, "/?", 2)) || (!_strnicmp(pszCmd, "/h", 2)))
|
||
{
|
||
Help();
|
||
return -1;
|
||
}
|
||
|
||
pszCmd = pszCmdNext;
|
||
}
|
||
|
||
|
||
g_iloop = execp.iloop;
|
||
g_oloop = execp.iloop;
|
||
g_hWndOther = execp.hWndOther;
|
||
|
||
|
||
if (g_fKicker)
|
||
{
|
||
g_WaitType = g_WaitType2;
|
||
g_SignalType = g_SignalType2;
|
||
if (!fFile)
|
||
_tcscpy(execp.szFile, TEXT("kicker"));
|
||
}
|
||
else
|
||
{
|
||
g_WaitType = g_WaitType1;
|
||
g_SignalType = g_SignalType1;
|
||
if (!fFile)
|
||
_tcscpy(execp.szFile, TEXT("waiter"));
|
||
|
||
}
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
|
||
DWORD CTaskSwitch::GetWaitType(LPSTR pszCmd)
|
||
{
|
||
ULONG i=0;
|
||
|
||
while (aszWait[++i]) // slot 0 is not used
|
||
{
|
||
if (!_stricmp(pszCmd, aszWait[i]))
|
||
return i;
|
||
}
|
||
|
||
Help();
|
||
return 0;
|
||
}
|
||
|
||
|
||
DWORD CTaskSwitch::GetSignalType(LPSTR pszCmd)
|
||
{
|
||
ULONG i=0;
|
||
|
||
while (aszSignal[++i]) // slot 0 is not used
|
||
{
|
||
if (!_stricmp(pszCmd, aszSignal[i]))
|
||
return i;
|
||
}
|
||
|
||
Help();
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// creates the command line parameters for the other guy
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
void CTaskSwitch::CreateOtherParms(void)
|
||
{
|
||
|
||
|
||
|
||
|
||
// write the formatted parms to the parm string
|
||
|
||
sprintf(g_szOtherParms, "/k %s %s /i:%d /o:%d /hWnd:%ld "
|
||
"/e1:%hs /e2:%hs /w1:%s /s1:%s /w2:%s /s2:%s",
|
||
(g_fThreadSwitch) ? "/t" : "/p",
|
||
(g_fWaitMultiple) ? "/m" : " ",
|
||
g_execp.iloop, // same loop counts as me
|
||
g_execp.oloop,
|
||
g_hWndMe, // posts to my window
|
||
g_execp.szSignalEvent, // it waits on my signal event
|
||
g_execp.szWaitEvent, // it signals my wait event
|
||
aszWait[g_WaitType1], // signal what i wait on
|
||
aszSignal[g_SignalType1], // wait on what i signal
|
||
aszWait[g_WaitType2], // signal what i wait on
|
||
aszSignal[g_SignalType2]); // wait on what i signal
|
||
|
||
}
|
||
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// writes the execution parameters to a log file.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
void CTaskSwitch::WriteExecParms()
|
||
{
|
||
// write the run parameters to the output file
|
||
|
||
g_output->WriteString(TEXT("Using Parametes:\n"));
|
||
g_output->WriteResult(TEXT("\tInner Loop Count = "), g_execp.iloop);
|
||
g_output->WriteResult(TEXT("\tOuter Loop Count = "), g_execp.oloop);
|
||
g_output->WriteString(TEXT("\n\n"));
|
||
|
||
// flush to avoid disk io during the test
|
||
g_output->Flush();
|
||
}
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// writes the results to a log file.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
void CTaskSwitch::WriteResults(void)
|
||
{
|
||
if (g_hr == S_OK)
|
||
{
|
||
// compute the averages
|
||
|
||
ULONG tTotal = 0;
|
||
|
||
// skip the first & last value as they are sometimes skewed
|
||
for (int i=0; i<g_execp.oloop; i++)
|
||
{
|
||
tTotal += g_time[i];
|
||
}
|
||
|
||
// compute average for 1 call/response
|
||
tTotal /= (g_execp.oloop * g_execp.iloop);
|
||
|
||
|
||
// display the results
|
||
|
||
g_output->WriteResults(TEXT("Times "), g_execp.oloop, g_time);
|
||
g_output->WriteResult(TEXT("\nAverage "), tTotal);
|
||
}
|
||
}
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// writes the help info to the screen
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
void CTaskSwitch::Help()
|
||
{
|
||
printf ("msgtask\n");
|
||
printf ("\t/o:<nnn> - outer loop count def 10\n");
|
||
printf ("\t/i:<nnn> - inner loop count def 100\n");
|
||
printf ("\t/f:<name> - name of output file. def [kick | wait]\n");
|
||
printf ("\t/w1:<event|getmsg|msgwait> - what to wait on\n");
|
||
printf ("\t/s1:<event|postmsg> - what to signal\n");
|
||
printf ("\t/w2:<event|getmsg|msgwait> - what to wait on\n");
|
||
printf ("\t/s2:<event|postmsg> - what to signal\n");
|
||
printf ("\t/e1:<name> - name of wait event\n");
|
||
printf ("\t/e2:<name> - name of signal event\n");
|
||
printf ("\t/k - kicker (as opposed to waiter)\n");
|
||
printf ("\t/t - use thread switching\n");
|
||
printf ("\t/p - use process switching\n");
|
||
printf ("\t/m - use WaitMultiple vs WaitSingle\n");
|
||
printf ("\t/hWnd:<nnnn> - window handle of other side\n");
|
||
|
||
printf ("\n");
|
||
printf ("timings are given for the inner loop count calls\n");
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//--------------------------------------------------------------------------
|
||
//
|
||
// creates a process
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
DWORD CTaskSwitch::CreateProc(void)
|
||
{
|
||
// create the command line
|
||
|
||
TCHAR szCmdLine[256];
|
||
|
||
_stprintf(szCmdLine, TEXT("ntsd procswap %hs"), g_szOtherParms);
|
||
|
||
|
||
|
||
// build the win32 startup info structure
|
||
|
||
STARTUPINFO startupinfo;
|
||
startupinfo.cb = sizeof(STARTUPINFO);
|
||
startupinfo.lpReserved = NULL;
|
||
startupinfo.lpDesktop = NULL;
|
||
startupinfo.lpTitle = TEXT("Task Switcher");
|
||
startupinfo.dwX = 40;
|
||
startupinfo.dwY = 40;
|
||
startupinfo.dwXSize = 80;
|
||
startupinfo.dwYSize = 40;
|
||
startupinfo.dwFlags = 0;
|
||
startupinfo.wShowWindow = SW_SHOWNORMAL;
|
||
startupinfo.cbReserved2 = 0;
|
||
startupinfo.lpReserved2 = NULL;
|
||
|
||
PROCESS_INFORMATION ProcInfo;
|
||
|
||
|
||
BOOL fRslt = CreateProcess(NULL, // app name
|
||
szCmdLine, // command line
|
||
NULL, // lpsaProcess
|
||
NULL, // lpsaThread
|
||
FALSE, // inherit handles
|
||
CREATE_NEW_CONSOLE,// creation flags
|
||
NULL, // lpEnvironment
|
||
NULL, // curr Dir
|
||
&startupinfo, // Startup Info
|
||
&ProcInfo); // process info
|
||
|
||
if (fRslt)
|
||
{
|
||
// we dont need the handles
|
||
CloseHandle(ProcInfo.hProcess);
|
||
CloseHandle(ProcInfo.hThread);
|
||
printf ("Created Process (%ws) pid=%x\n", szCmdLine, ProcInfo.dwProcessId);
|
||
return S_OK;
|
||
}
|
||
else
|
||
{
|
||
// what went wrong?
|
||
DWORD dwRet = GetLastError();
|
||
printf ("CreateProcess (%ws) failed %x\n", szCmdLine, dwRet);
|
||
return dwRet;
|
||
}
|
||
}
|
||
|