1 | //////////////////////////////////////////////////////////// |
---|
2 | // Copyright (C) Roman Ryltsov, 2008-2011 |
---|
3 | // Created by Roman Ryltsov roman@alax.info |
---|
4 | // |
---|
5 | // $Id: EventSynchronizationTest01.cpp 246 2011-03-19 17:24:45Z alax $ |
---|
6 | |
---|
7 | #include "stdafx.h" |
---|
8 | #if defined(_DEBUG) |
---|
9 | #include <conio.h> |
---|
10 | #endif // defined(_DEBUG) |
---|
11 | #include <winternl.h> |
---|
12 | #include <atlsync.h> |
---|
13 | |
---|
14 | static const UINT g_nMethod = 4; |
---|
15 | static const SIZE_T g_nSendThreadCount = 3; |
---|
16 | static const ULONG g_nDuration = 60 * 1000; // 60 seconds |
---|
17 | |
---|
18 | #pragma region Internals |
---|
19 | |
---|
20 | namespace Internals |
---|
21 | { |
---|
22 | #if !defined(_WIN64) |
---|
23 | #error The undocumented structures below are only valid for 64-bit code |
---|
24 | #endif // !defined(_WIN64) |
---|
25 | |
---|
26 | //typedef struct _IO_COUNTERS |
---|
27 | //{ |
---|
28 | // ULONG ReadOperationCount; |
---|
29 | // ULONG WriteOperationCount; |
---|
30 | // ULONG OtherOperationCount; |
---|
31 | // ULONGLONG ReadTransferCount; |
---|
32 | // ULONGLONG WriteTransferCount; |
---|
33 | // ULONGLONG OtherTransferCount; |
---|
34 | //} IO_COUNTERS; |
---|
35 | |
---|
36 | typedef struct _VM_COUNTERS |
---|
37 | { |
---|
38 | ULONGLONG PeakVirtualSize; |
---|
39 | ULONGLONG VirtualSize; |
---|
40 | ULONG PageFaultCount; |
---|
41 | ULONG Reserved1; |
---|
42 | ULONGLONG PeakWorkingSetSize; |
---|
43 | ULONGLONG WorkingSetSize; |
---|
44 | ULONGLONG QuotaPeakPagedPoolUsage; |
---|
45 | ULONGLONG QuotaPagedPoolUsage; |
---|
46 | ULONGLONG QuotaPeakNonPagedPoolUsage; |
---|
47 | ULONGLONG QuotaNonPagedPoolUsage; |
---|
48 | ULONGLONG PagefileUsage; |
---|
49 | ULONGLONG PeakPagefileUsage; |
---|
50 | ULONGLONG Reserved2; |
---|
51 | } VM_COUNTERS; |
---|
52 | |
---|
53 | typedef struct _CLIENT_ID |
---|
54 | { |
---|
55 | HANDLE ProcessId; |
---|
56 | HANDLE ThreadId; |
---|
57 | } CLIENT_ID, *PCLIENT_ID; |
---|
58 | |
---|
59 | typedef LONG KPRIORITY; |
---|
60 | typedef LONG KWAIT_REASON; |
---|
61 | |
---|
62 | typedef struct _SYSTEM_THREAD_INFORMATION { |
---|
63 | ULONGLONG KernelTime; |
---|
64 | ULONGLONG UserTime; |
---|
65 | ULONGLONG CreateTime; |
---|
66 | ULONG WaitTime; |
---|
67 | ULONG Reserved1; |
---|
68 | PVOID StartAddress; |
---|
69 | CLIENT_ID ClientId; |
---|
70 | KPRIORITY Priority; |
---|
71 | LONG BasePriority; |
---|
72 | ULONG ContextSwitchCount; |
---|
73 | ULONG State; |
---|
74 | KWAIT_REASON WaitReason; |
---|
75 | } SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; |
---|
76 | |
---|
77 | typedef struct _SYSTEM_PROCESS_INFORMATION { |
---|
78 | ULONG NextEntryOffset; |
---|
79 | ULONG NumberOfThreads; |
---|
80 | ULONGLONG Reserved[3]; |
---|
81 | ULONGLONG CreateTime; |
---|
82 | ULONGLONG UserTime; |
---|
83 | ULONGLONG KernelTime; |
---|
84 | UNICODE_STRING ImageName; |
---|
85 | KPRIORITY BasePriority; |
---|
86 | HANDLE ProcessId; |
---|
87 | HANDLE InheritedFromProcessId; |
---|
88 | ULONG HandleCount; |
---|
89 | ULONG Reserved2[2]; |
---|
90 | ULONG PrivatePageCount; // Garbage |
---|
91 | VM_COUNTERS VirtualMemoryCounters; |
---|
92 | IO_COUNTERS IoCounters; |
---|
93 | SYSTEM_THREAD_INFORMATION Threads[1]; |
---|
94 | } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; |
---|
95 | |
---|
96 | class CSystemProcessInformation |
---|
97 | { |
---|
98 | private: |
---|
99 | CHeapPtr<BYTE> m_pnData; |
---|
100 | SIZE_T m_nDataSize; |
---|
101 | |
---|
102 | public: |
---|
103 | // CSystemProcessInformation |
---|
104 | CSystemProcessInformation() : |
---|
105 | m_nDataSize(0) |
---|
106 | { |
---|
107 | } |
---|
108 | VOID Initialize() |
---|
109 | { |
---|
110 | static const SIZE_T g_nDataCapacity = 1 << 20; // 1MB |
---|
111 | ATLENSURE_THROW(m_pnData.Reallocate(g_nDataCapacity), E_OUTOFMEMORY); |
---|
112 | ULONG nDataSize = 0; |
---|
113 | typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(__in SYSTEM_INFORMATION_CLASS SystemInformationClass, __inout PVOID SystemInformation, __in ULONG SystemInformationLength, __out_opt PULONG ReturnLength); |
---|
114 | NTQUERYSYSTEMINFORMATION NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION) GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "NtQuerySystemInformation"); |
---|
115 | ATLASSERT(NtQuerySystemInformation); |
---|
116 | ATLVERIFY(NtQuerySystemInformation(SystemProcessInformation, m_pnData, (ULONG) g_nDataCapacity, &nDataSize) == 0); |
---|
117 | m_nDataSize = nDataSize; |
---|
118 | } |
---|
119 | const SYSTEM_PROCESS_INFORMATION* LookupProcess(DWORD nProcessIdentifier = GetCurrentProcessId()) throw() |
---|
120 | { |
---|
121 | if(m_nDataSize) |
---|
122 | for(const SYSTEM_PROCESS_INFORMATION* pProcessInformation = (const SYSTEM_PROCESS_INFORMATION*) (const BYTE*) m_pnData; pProcessInformation; pProcessInformation = pProcessInformation->NextEntryOffset ? (const SYSTEM_PROCESS_INFORMATION*) ((const BYTE*) pProcessInformation + pProcessInformation->NextEntryOffset) : NULL) |
---|
123 | if(pProcessInformation->ProcessId == (HANDLE) nProcessIdentifier) |
---|
124 | return pProcessInformation; |
---|
125 | return NULL; |
---|
126 | } |
---|
127 | static const SYSTEM_THREAD_INFORMATION* LookupThread(const SYSTEM_PROCESS_INFORMATION* pProcessInformation, DWORD nThreadIdentifier = GetCurrentThreadId()) throw() |
---|
128 | { |
---|
129 | if(pProcessInformation) |
---|
130 | for(ULONG nThreadIndex = 0; nThreadIndex < pProcessInformation->NumberOfThreads; nThreadIndex++) |
---|
131 | { |
---|
132 | const SYSTEM_THREAD_INFORMATION* pThreadInformation = &pProcessInformation->Threads[nThreadIndex]; |
---|
133 | ATLASSERT(pThreadInformation->ClientId.ProcessId == pProcessInformation->ProcessId); |
---|
134 | if(pThreadInformation->ClientId.ThreadId == (HANDLE) nThreadIdentifier) |
---|
135 | return pThreadInformation; |
---|
136 | } |
---|
137 | return NULL; |
---|
138 | } |
---|
139 | }; |
---|
140 | } |
---|
141 | |
---|
142 | #pragma endregion |
---|
143 | |
---|
144 | class CModule |
---|
145 | { |
---|
146 | private: |
---|
147 | CEvent m_StartEvent; |
---|
148 | CEvent m_StopEvent; |
---|
149 | CComAutoCriticalSection m_CriticalSection; |
---|
150 | CEvent m_AvailabilityEvent; |
---|
151 | UINT m_nSendCount; |
---|
152 | UINT m_nReceiveCount; |
---|
153 | |
---|
154 | DWORD SendThreadProc() |
---|
155 | { |
---|
156 | UINT nRandomValue = (UINT) rand(); |
---|
157 | const HANDLE phObjects[] = { m_StopEvent }; |
---|
158 | ATLVERIFY(WaitForSingleObject(m_StartEvent, INFINITE) == WAIT_OBJECT_0); |
---|
159 | for(UINT nIteration = 0; ; nIteration++) |
---|
160 | { |
---|
161 | const INT nDelay = (INT) (nRandomValue % 1000) - 998; |
---|
162 | nRandomValue = (nRandomValue * 13) ^ ((nRandomValue * 17) << 2) ^ ((nRandomValue * 19) << 4); |
---|
163 | if(nDelay > 0) |
---|
164 | { |
---|
165 | const DWORD nWaitResult = WaitForMultipleObjects(DIM(phObjects), phObjects, FALSE, nDelay); |
---|
166 | ATLASSERT(nWaitResult - WAIT_OBJECT_0 < DIM(phObjects) || nWaitResult == WAIT_TIMEOUT); |
---|
167 | if(nWaitResult == WAIT_OBJECT_0) |
---|
168 | break; |
---|
169 | } |
---|
170 | #pragma endregion |
---|
171 | switch(g_nMethod) |
---|
172 | { |
---|
173 | case 1: |
---|
174 | { |
---|
175 | CComCritSecLock<CComAutoCriticalSection> DataLock(m_CriticalSection); |
---|
176 | m_nSendCount++; |
---|
177 | ATLVERIFY(m_AvailabilityEvent.Set()); |
---|
178 | } |
---|
179 | break; |
---|
180 | case 2: |
---|
181 | { |
---|
182 | { |
---|
183 | CComCritSecLock<CComAutoCriticalSection> DataLock(m_CriticalSection); |
---|
184 | m_nSendCount++; |
---|
185 | } |
---|
186 | ATLVERIFY(m_AvailabilityEvent.Set()); |
---|
187 | } |
---|
188 | break; |
---|
189 | case 3: |
---|
190 | case 4: |
---|
191 | { |
---|
192 | //CComCritSecLock<CComAutoCriticalSection> DataLock(m_CriticalSection); |
---|
193 | m_nSendCount++; |
---|
194 | ATLVERIFY(m_AvailabilityEvent.Set()); |
---|
195 | } |
---|
196 | break; |
---|
197 | default: |
---|
198 | ATLASSUME(FALSE); |
---|
199 | } |
---|
200 | } |
---|
201 | // NOTE: Master thread will want to query thread performance |
---|
202 | Sleep(10000); |
---|
203 | return 0; |
---|
204 | } |
---|
205 | DWORD ReceiveThreadProc() |
---|
206 | { |
---|
207 | const HANDLE phObjects[] = { m_StopEvent, m_AvailabilityEvent }; |
---|
208 | ATLVERIFY(WaitForSingleObject(m_StartEvent, INFINITE) == WAIT_OBJECT_0); |
---|
209 | for(UINT nIteration = 0; ; nIteration++) |
---|
210 | { |
---|
211 | const DWORD nWaitResult = WaitForMultipleObjects(DIM(phObjects), phObjects, FALSE, INFINITE); |
---|
212 | ATLASSERT(nWaitResult - WAIT_OBJECT_0 < DIM(phObjects) || nWaitResult == WAIT_TIMEOUT); |
---|
213 | if(nWaitResult == WAIT_OBJECT_0) |
---|
214 | break; |
---|
215 | switch(g_nMethod) |
---|
216 | { |
---|
217 | case 1: |
---|
218 | case 2: |
---|
219 | case 4: |
---|
220 | { |
---|
221 | CComCritSecLock<CComAutoCriticalSection> DataLock(m_CriticalSection); |
---|
222 | m_nReceiveCount++; |
---|
223 | ATLVERIFY(m_AvailabilityEvent.Reset()); |
---|
224 | } |
---|
225 | break; |
---|
226 | case 3: |
---|
227 | { |
---|
228 | //CComCritSecLock<CComAutoCriticalSection> DataLock(m_CriticalSection); |
---|
229 | m_nReceiveCount++; |
---|
230 | ATLVERIFY(m_AvailabilityEvent.Reset()); |
---|
231 | } |
---|
232 | break; |
---|
233 | default: |
---|
234 | ATLASSUME(FALSE); |
---|
235 | } |
---|
236 | } |
---|
237 | // NOTE: Master thread will want to query thread performance |
---|
238 | Sleep(10000); |
---|
239 | return 0; |
---|
240 | } |
---|
241 | static DWORD STDMETHODCALLTYPE SendThreadProc(CModule* pModule) |
---|
242 | { |
---|
243 | return pModule->SendThreadProc(); |
---|
244 | } |
---|
245 | static DWORD STDMETHODCALLTYPE ReceiveThreadProc(CModule* pModule) |
---|
246 | { |
---|
247 | return pModule->ReceiveThreadProc(); |
---|
248 | } |
---|
249 | static ULONG FileTimesToMilliseconds(const FILETIME& AnchorFileTime, const FILETIME& FileTime) throw() |
---|
250 | { |
---|
251 | return (ULONG) ((reinterpret_cast<const ULONGLONG&>(FileTime) - reinterpret_cast<const ULONGLONG&>(AnchorFileTime)) / 10000); |
---|
252 | } |
---|
253 | static ULONG FileTimesToMicroseconds(const FILETIME& AnchorFileTime, const FILETIME& FileTime) throw() |
---|
254 | { |
---|
255 | return (ULONG) ((reinterpret_cast<const ULONGLONG&>(FileTime) - reinterpret_cast<const ULONGLONG&>(AnchorFileTime)) / 10); |
---|
256 | } |
---|
257 | template <typename _Integer> |
---|
258 | CString FormatIntegerNumber(_Integer nValue) |
---|
259 | { |
---|
260 | CString sValue; |
---|
261 | sValue.Format(_T("%d"), nValue); |
---|
262 | TCHAR pszValue[32] = { 0 }; |
---|
263 | NUMBERFMT Format = { 0, 1, 3, _T("."), _T(","), 1 }; |
---|
264 | ATLVERIFY(GetNumberFormat(LOCALE_USER_DEFAULT, 0, sValue, &Format, pszValue, DIM(pszValue))); |
---|
265 | return pszValue; |
---|
266 | } |
---|
267 | |
---|
268 | public: |
---|
269 | // CModule |
---|
270 | VOID Run() |
---|
271 | { |
---|
272 | srand(1); |
---|
273 | ATLVERIFY(SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)); |
---|
274 | ATLVERIFY(SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)); |
---|
275 | _tprintf(_T("Method: %d\n"), g_nMethod); |
---|
276 | _tprintf(_T("Send Thread Count: %d\n"), g_nSendThreadCount); |
---|
277 | _tprintf(_T("Duration: %s ms\n"), FormatIntegerNumber(g_nDuration)); |
---|
278 | _tprintf(_T("* * *\n")); |
---|
279 | m_nSendCount = 0; |
---|
280 | m_nReceiveCount = 0; |
---|
281 | ATLENSURE_THROW(m_StartEvent.Create(NULL, TRUE, FALSE, NULL), AtlHresultFromLastError()); |
---|
282 | ATLENSURE_THROW(m_AvailabilityEvent.Create(NULL, TRUE, FALSE, NULL), AtlHresultFromLastError()); |
---|
283 | ATLENSURE_THROW(m_StopEvent.Create(NULL, TRUE, FALSE, NULL), AtlHresultFromLastError()); |
---|
284 | CAtlList<CHandle> SendThreadList; |
---|
285 | for(SIZE_T nIndex = 0; nIndex < g_nSendThreadCount; nIndex++) |
---|
286 | { |
---|
287 | CHandle& SendThread = SendThreadList.GetAt(SendThreadList.AddTail()); |
---|
288 | SendThread.Attach(AtlCreateThread(&CModule::SendThreadProc, this)); |
---|
289 | ATLVERIFY(SetThreadPriority(SendThread, THREAD_PRIORITY_ABOVE_NORMAL)); |
---|
290 | } |
---|
291 | CHandle& SendThread = SendThreadList.GetHead(); |
---|
292 | CHandle ReceiveThread; |
---|
293 | ReceiveThread.Attach(AtlCreateThread(&CModule::ReceiveThreadProc, this)); |
---|
294 | ATLVERIFY(SetThreadPriority(ReceiveThread, THREAD_PRIORITY_ABOVE_NORMAL)); |
---|
295 | Sleep(1000); |
---|
296 | FILETIME SendThreadCreationTime, SendThreadExitTime, AnchorSendThreadKernelTime, AnchorSendThreadUserTime; |
---|
297 | ATLVERIFY(GetThreadTimes(SendThread, &SendThreadCreationTime, &SendThreadExitTime, &AnchorSendThreadKernelTime, &AnchorSendThreadUserTime)); |
---|
298 | FILETIME ReceiveThreadCreationTime, ReceiveThreadExitTime, AnchorReceiveThreadKernelTime, AnchorReceiveThreadUserTime; |
---|
299 | ATLVERIFY(GetThreadTimes(ReceiveThread, &ReceiveThreadCreationTime, &ReceiveThreadExitTime, &AnchorReceiveThreadKernelTime, &AnchorReceiveThreadUserTime)); |
---|
300 | FILETIME ProcessCreationTime, ProcessExitTime, AnchorProcessKernelTime, AnchorProcessUserTime; |
---|
301 | ATLVERIFY(GetProcessTimes(GetCurrentProcess(), &ProcessCreationTime, &ProcessExitTime, &AnchorProcessKernelTime, &AnchorProcessUserTime)); |
---|
302 | ATLVERIFY(m_StartEvent.Set()); |
---|
303 | Sleep(g_nDuration); |
---|
304 | ATLVERIFY(m_StopEvent.Set()); |
---|
305 | Sleep(1000); |
---|
306 | #pragma region Performance Statistics |
---|
307 | FILETIME ProcessKernelTime, ProcessUserTime; |
---|
308 | ATLVERIFY(GetProcessTimes(GetCurrentProcess(), &ProcessCreationTime, &ProcessExitTime, &ProcessKernelTime, &ProcessUserTime)); |
---|
309 | FILETIME SendThreadKernelTime, SendThreadUserTime; |
---|
310 | ATLVERIFY(GetThreadTimes(SendThread, &SendThreadCreationTime, &SendThreadExitTime, &SendThreadKernelTime, &SendThreadUserTime)); |
---|
311 | FILETIME ReceiveThreadKernelTime, ReceiveThreadUserTime; |
---|
312 | ATLVERIFY(GetThreadTimes(ReceiveThread, &ReceiveThreadCreationTime, &ReceiveThreadExitTime, &ReceiveThreadKernelTime, &ReceiveThreadUserTime)); |
---|
313 | _tprintf(_T("Send Count: %15s\n"), FormatIntegerNumber(m_nSendCount)); |
---|
314 | _tprintf(_T("Receive Count: %15s\n"), FormatIntegerNumber(m_nReceiveCount)); |
---|
315 | _tprintf(_T("\n")); |
---|
316 | Internals::CSystemProcessInformation SystemProcessInformation; |
---|
317 | SystemProcessInformation.Initialize(); |
---|
318 | const Internals::SYSTEM_PROCESS_INFORMATION* pProcessInformation = SystemProcessInformation.LookupProcess(); |
---|
319 | const Internals::SYSTEM_THREAD_INFORMATION* pSendThreadInformation = NULL; |
---|
320 | const Internals::SYSTEM_THREAD_INFORMATION* pReceiveThreadInformation = NULL; |
---|
321 | if(pProcessInformation) |
---|
322 | { |
---|
323 | //const ::SYSTEM_PROCESS_INFORMATION* pAnotherProcessInformation = (const ::SYSTEM_PROCESS_INFORMATION*) pProcessInformation; |
---|
324 | pSendThreadInformation = Internals::CSystemProcessInformation::LookupThread(pProcessInformation, GetThreadId(SendThread)); |
---|
325 | pReceiveThreadInformation = Internals::CSystemProcessInformation::LookupThread(pProcessInformation, GetThreadId(ReceiveThread)); |
---|
326 | } |
---|
327 | _tprintf(_T("Process Time: User %10s ms, Kernel %10s ms\n"), |
---|
328 | FormatIntegerNumber(FileTimesToMilliseconds(AnchorProcessUserTime, ProcessUserTime)), |
---|
329 | FormatIntegerNumber(FileTimesToMilliseconds(AnchorProcessKernelTime, ProcessKernelTime)), |
---|
330 | 0); |
---|
331 | _tprintf(_T("Send Thread 0 Time: User %10s ms, Kernel %10s ms, Context Switches %15s\n"), |
---|
332 | FormatIntegerNumber(FileTimesToMilliseconds(AnchorSendThreadUserTime, SendThreadUserTime)), |
---|
333 | FormatIntegerNumber(FileTimesToMilliseconds(AnchorSendThreadKernelTime, SendThreadKernelTime)), |
---|
334 | FormatIntegerNumber(pSendThreadInformation ? pSendThreadInformation->ContextSwitchCount : 0), |
---|
335 | 0); |
---|
336 | _tprintf(_T("Receive Thread Time: User %10s ms, Kernel %10s ms, Context Switches %15s\n"), |
---|
337 | FormatIntegerNumber(FileTimesToMilliseconds(AnchorReceiveThreadUserTime, ReceiveThreadUserTime)), |
---|
338 | FormatIntegerNumber(FileTimesToMilliseconds(AnchorReceiveThreadKernelTime, ReceiveThreadKernelTime)), |
---|
339 | FormatIntegerNumber(pReceiveThreadInformation ? pReceiveThreadInformation->ContextSwitchCount : 0), |
---|
340 | 0); |
---|
341 | #pragma endregion |
---|
342 | } |
---|
343 | }; |
---|
344 | |
---|
345 | int _tmain(int argc, _TCHAR* argv[]) |
---|
346 | { |
---|
347 | _ATLTRY |
---|
348 | { |
---|
349 | CModule Module; |
---|
350 | Module.Run(); |
---|
351 | } |
---|
352 | _ATLCATCHALL() |
---|
353 | { |
---|
354 | _tprintf(_T("Fatal Error\n")); |
---|
355 | } |
---|
356 | return 0; |
---|
357 | } |
---|
358 | |
---|