EDN Admin
Well-known member
[This is Win7/64 bit running on 6-core Intel desktop]
We are building test framework and need to run in parallel same executable against different test files. Once executable is finished we look at GetExitCodeProcess result.
Problem is that exit code for the process retrieved via GetExitCodeProcess rarely(1 in ~2000 invocations) but guaranteed to return 0 instead of expected 1.
In addition GetExitCodeThread for the main thread with higher frequency(1 in ~500 invocations) returns 0xc000004b (<span style="color:#222222; font-family:Arial,Helvetica,sans-serif; font-size:13px; line-height:normal STATUS_THREAD_IS_TERMINATING), versus
expected return value of 1.
<span style="color:#222222; font-family:Arial,Helvetica,sans-serif; font-size:13px; line-height:normal So I put together small vc++ program which recursively executes itself given number of times deep and wide(instances invoked in parallel on top level).
<span style="color:#222222; font-family:Arial,Helvetica,sans-serif; font-size:13px; line-height:normal When there is no parallelism ("testec <n> 1") it can go as deep as you want - no errors regardless of how large <n> is.
<span style="color:#222222; font-family:Arial,Helvetica,sans-serif; font-size:13px; line-height:normal When you start increasing number of parallel instances("testec <n> 10") - more instances you have sooner and more problems you will see.
<span style="color:#222222; font-size:small <span style="line-height:normal This points to some race condition on exiting the process/thread, but what exactly is going wrong - this is where I need help, please!
<span style="color:#222222; font-size:small <span style="line-height:normal Couple more notes: this program works without any issues on Debug/Checked build of Win7/64 bit and seems to work fine on slower MacBook Pro.
Here is the code:
<pre class="prettyprint #include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>
int depth;
void DisplayError(char *pszAPI)
{
LPVOID lpvMessageBuffer;
TCHAR szPrintBuffer[512];
DWORD nCharsWritten;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpvMessageBuffer, 0, NULL);
const size_t size = 1024;
WCHAR wszAPI[size];
mbstowcs(wszAPI, pszAPI, strlen(pszAPI));
_stprintf(
szPrintBuffer,
_T("ERROR: %s: error code: %d. message: %s.n"),
wszAPI,
GetLastError(),
(char*)lpvMessageBuffer);
WriteConsole(
GetStdHandle(STD_OUTPUT_HANDLE),
szPrintBuffer,
lstrlen(szPrintBuffer),
&nCharsWritten,
NULL);
LocalFree(lpvMessageBuffer);
ExitProcess(GetLastError());
}
void Display(char* fmt,...) {
const size_t size = 1024;
char buf[size];
va_list ap;
va_start(ap, fmt);
int n = vsnprintf(buf, size, fmt, ap);
va_end(ap);
DWORD nCharsWritten;
char fullbuf[size];
sprintf(fullbuf, "%*s", depth, "");
strcat(fullbuf, buf);
//printf("%s", fullbuf);
FILE* f = fopen("trace.log", "a+");
fprintf(f, "%s", fullbuf);
fclose(f);
va_end(ap);
}
void error(char* fmt,...) {
const size_t size = 1024;
char buf[size];
va_list ap;
va_start(ap, fmt);
int n = vsnprintf(buf, size, fmt, ap);
va_end(ap);
DWORD nCharsWritten;
char fullbuf[size];
sprintf(fullbuf, "%*s", depth, "");
strcat(fullbuf, buf);
//printf("%s", fullbuf);
FILE* f = fopen("trace-error.log", "a+");
fprintf(f, "%s", fullbuf);
fclose(f);
va_end(ap);
}
int _tmain(int argc, _TCHAR* argv[]) {
if (argc < 2) {
Display("testec <depth> [<multiply>]n");
return -1;
}
depth = _ttoi(argv[1]);
SetConsoleOutputCP(CP_OEMCP);
Display(
"%d(%d) Depth: %d Starting stdin=%d stdout=%d stderr=%dn",
GetCurrentProcessId(),
GetCurrentThreadId(),
depth,
GetStdHandle(STD_INPUT_HANDLE),
GetStdHandle(STD_OUTPUT_HANDLE),
GetStdHandle(STD_ERROR_HANDLE));
if (depth == 0) {
Display("%d(%d)************n",
GetCurrentProcessId(), GetCurrentThreadId());
return 1;
}
int multiply = 1;
if (argc > 2) {
multiply = _ttoi(argv[2]);
if (multiply < 1) {
error("multiply should be greater than 1n");
return -2;
}
else if (multiply > MAXIMUM_WAIT_OBJECTS / 2) {
error(
"multiply should not be greater than %dn",
MAXIMUM_WAIT_OBJECTS / 2);
return -2;
}
}
HANDLE* hs = new HANDLE[multiply * 2];
DWORD* ids = new DWORD[multiply * 2];
for (int i = 0; i < multiply; i++) {
Display(
"%d(%d) Multiply %d out of %dn",
GetCurrentProcessId(), GetCurrentThreadId(),
i, multiply);
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = FALSE;
STARTUPINFO startup_info;
ZeroMemory(&startup_info, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
startup_info.dwFlags = STARTF_USESTDHANDLES;
PROCESS_INFORMATION process_info;
ZeroMemory(&process_info, sizeof(process_info));
TCHAR cmd[1024];
_stprintf(cmd, _T("%s %d"), argv[0], depth-1);
if (!CreateProcess(NULL, cmd, NULL, NULL,
TRUE, 0, NULL, NULL, &startup_info,
&process_info)) {
error("Failed to create process");
}
hs[i*2] = process_info.hProcess;
hs[i*2+1] = process_info.hThread;
ids[i*2] = process_info.dwProcessId;
ids[i*2+1] = process_info.dwThreadId;
}
DWORD rc = WaitForMultipleObjects(
multiply*2, hs, true, INFINITE);
if (rc != WAIT_OBJECT_0) {
error(
"WaitForMultipleObjects failed. RC=%x. GetLastError=%xn",
rc, GetLastError());
}
DWORD exit_code;
for (int i = 0; i < multiply; i++) {
DWORD texit_code;
if (!GetExitCodeThread(hs[i*2+1], reinterpret_cast<DWORD*>(&texit_code))) error("*** GetExitCodeThread failed %dn", GetLastError());
if (!GetExitCodeProcess(hs[i*2], reinterpret_cast<DWORD*>(&exit_code))) error("*** GetExitCodeProcess failed %dn", GetLastError());
if (texit_code != exit_code) {
error("*** Thread %d exit code %x didnt match process %d exit code %xn", ids[i*2+1], texit_code, ids[i*2], exit_code);
}
if (!CloseHandle(hs[i*2+1])) {
printf("CloseHandle failed");
}
if (!CloseHandle(hs[i*2])) {
printf("CloseHandle failed");
}
if (exit_code != 1) {
error("*** Failed to get correct exit code. Got %d insteadn", exit_code);
}
}
free(ids);
free(hs);
return exit_code;
}[/code]
<br/>
Here is simple t.bat, which runs this testec.exe repeatedly and prints out trace-error.log file:
<pre class="prettyprint @echo off
:a
if exist trace-error.log del trace-error.log
debugtestec %*
echo ---
if exist trace-error.log type trace-error.log
goto a
[/code]
<br/>
And here is sample output with errors
<pre class="prettyprint C:testectestec>t 2 30
---
---
---
---
---
---
---
---
*** Thread 28232 exit code 1 didnt match process 8260 exit code 0
*** Failed to get correct exit code. Got 0 instead
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
*** Thread 27320 exit code c000004b didnt match process 12260 exit code 1
---
---
*** Thread 35552 exit code c000004b didnt match process 23080 exit code 1
---
---
---
---
---
---
---
---[/code]
<br/>
Thank you in advance!
View the full article
We are building test framework and need to run in parallel same executable against different test files. Once executable is finished we look at GetExitCodeProcess result.
Problem is that exit code for the process retrieved via GetExitCodeProcess rarely(1 in ~2000 invocations) but guaranteed to return 0 instead of expected 1.
In addition GetExitCodeThread for the main thread with higher frequency(1 in ~500 invocations) returns 0xc000004b (<span style="color:#222222; font-family:Arial,Helvetica,sans-serif; font-size:13px; line-height:normal STATUS_THREAD_IS_TERMINATING), versus
expected return value of 1.
<span style="color:#222222; font-family:Arial,Helvetica,sans-serif; font-size:13px; line-height:normal So I put together small vc++ program which recursively executes itself given number of times deep and wide(instances invoked in parallel on top level).
<span style="color:#222222; font-family:Arial,Helvetica,sans-serif; font-size:13px; line-height:normal When there is no parallelism ("testec <n> 1") it can go as deep as you want - no errors regardless of how large <n> is.
<span style="color:#222222; font-family:Arial,Helvetica,sans-serif; font-size:13px; line-height:normal When you start increasing number of parallel instances("testec <n> 10") - more instances you have sooner and more problems you will see.
<span style="color:#222222; font-size:small <span style="line-height:normal This points to some race condition on exiting the process/thread, but what exactly is going wrong - this is where I need help, please!
<span style="color:#222222; font-size:small <span style="line-height:normal Couple more notes: this program works without any issues on Debug/Checked build of Win7/64 bit and seems to work fine on slower MacBook Pro.
Here is the code:
<pre class="prettyprint #include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>
int depth;
void DisplayError(char *pszAPI)
{
LPVOID lpvMessageBuffer;
TCHAR szPrintBuffer[512];
DWORD nCharsWritten;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpvMessageBuffer, 0, NULL);
const size_t size = 1024;
WCHAR wszAPI[size];
mbstowcs(wszAPI, pszAPI, strlen(pszAPI));
_stprintf(
szPrintBuffer,
_T("ERROR: %s: error code: %d. message: %s.n"),
wszAPI,
GetLastError(),
(char*)lpvMessageBuffer);
WriteConsole(
GetStdHandle(STD_OUTPUT_HANDLE),
szPrintBuffer,
lstrlen(szPrintBuffer),
&nCharsWritten,
NULL);
LocalFree(lpvMessageBuffer);
ExitProcess(GetLastError());
}
void Display(char* fmt,...) {
const size_t size = 1024;
char buf[size];
va_list ap;
va_start(ap, fmt);
int n = vsnprintf(buf, size, fmt, ap);
va_end(ap);
DWORD nCharsWritten;
char fullbuf[size];
sprintf(fullbuf, "%*s", depth, "");
strcat(fullbuf, buf);
//printf("%s", fullbuf);
FILE* f = fopen("trace.log", "a+");
fprintf(f, "%s", fullbuf);
fclose(f);
va_end(ap);
}
void error(char* fmt,...) {
const size_t size = 1024;
char buf[size];
va_list ap;
va_start(ap, fmt);
int n = vsnprintf(buf, size, fmt, ap);
va_end(ap);
DWORD nCharsWritten;
char fullbuf[size];
sprintf(fullbuf, "%*s", depth, "");
strcat(fullbuf, buf);
//printf("%s", fullbuf);
FILE* f = fopen("trace-error.log", "a+");
fprintf(f, "%s", fullbuf);
fclose(f);
va_end(ap);
}
int _tmain(int argc, _TCHAR* argv[]) {
if (argc < 2) {
Display("testec <depth> [<multiply>]n");
return -1;
}
depth = _ttoi(argv[1]);
SetConsoleOutputCP(CP_OEMCP);
Display(
"%d(%d) Depth: %d Starting stdin=%d stdout=%d stderr=%dn",
GetCurrentProcessId(),
GetCurrentThreadId(),
depth,
GetStdHandle(STD_INPUT_HANDLE),
GetStdHandle(STD_OUTPUT_HANDLE),
GetStdHandle(STD_ERROR_HANDLE));
if (depth == 0) {
Display("%d(%d)************n",
GetCurrentProcessId(), GetCurrentThreadId());
return 1;
}
int multiply = 1;
if (argc > 2) {
multiply = _ttoi(argv[2]);
if (multiply < 1) {
error("multiply should be greater than 1n");
return -2;
}
else if (multiply > MAXIMUM_WAIT_OBJECTS / 2) {
error(
"multiply should not be greater than %dn",
MAXIMUM_WAIT_OBJECTS / 2);
return -2;
}
}
HANDLE* hs = new HANDLE[multiply * 2];
DWORD* ids = new DWORD[multiply * 2];
for (int i = 0; i < multiply; i++) {
Display(
"%d(%d) Multiply %d out of %dn",
GetCurrentProcessId(), GetCurrentThreadId(),
i, multiply);
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = FALSE;
STARTUPINFO startup_info;
ZeroMemory(&startup_info, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
startup_info.dwFlags = STARTF_USESTDHANDLES;
PROCESS_INFORMATION process_info;
ZeroMemory(&process_info, sizeof(process_info));
TCHAR cmd[1024];
_stprintf(cmd, _T("%s %d"), argv[0], depth-1);
if (!CreateProcess(NULL, cmd, NULL, NULL,
TRUE, 0, NULL, NULL, &startup_info,
&process_info)) {
error("Failed to create process");
}
hs[i*2] = process_info.hProcess;
hs[i*2+1] = process_info.hThread;
ids[i*2] = process_info.dwProcessId;
ids[i*2+1] = process_info.dwThreadId;
}
DWORD rc = WaitForMultipleObjects(
multiply*2, hs, true, INFINITE);
if (rc != WAIT_OBJECT_0) {
error(
"WaitForMultipleObjects failed. RC=%x. GetLastError=%xn",
rc, GetLastError());
}
DWORD exit_code;
for (int i = 0; i < multiply; i++) {
DWORD texit_code;
if (!GetExitCodeThread(hs[i*2+1], reinterpret_cast<DWORD*>(&texit_code))) error("*** GetExitCodeThread failed %dn", GetLastError());
if (!GetExitCodeProcess(hs[i*2], reinterpret_cast<DWORD*>(&exit_code))) error("*** GetExitCodeProcess failed %dn", GetLastError());
if (texit_code != exit_code) {
error("*** Thread %d exit code %x didnt match process %d exit code %xn", ids[i*2+1], texit_code, ids[i*2], exit_code);
}
if (!CloseHandle(hs[i*2+1])) {
printf("CloseHandle failed");
}
if (!CloseHandle(hs[i*2])) {
printf("CloseHandle failed");
}
if (exit_code != 1) {
error("*** Failed to get correct exit code. Got %d insteadn", exit_code);
}
}
free(ids);
free(hs);
return exit_code;
}[/code]
<br/>
Here is simple t.bat, which runs this testec.exe repeatedly and prints out trace-error.log file:
<pre class="prettyprint @echo off
:a
if exist trace-error.log del trace-error.log
debugtestec %*
echo ---
if exist trace-error.log type trace-error.log
goto a
[/code]
<br/>
And here is sample output with errors
<pre class="prettyprint C:testectestec>t 2 30
---
---
---
---
---
---
---
---
*** Thread 28232 exit code 1 didnt match process 8260 exit code 0
*** Failed to get correct exit code. Got 0 instead
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
*** Thread 27320 exit code c000004b didnt match process 12260 exit code 1
---
---
*** Thread 35552 exit code c000004b didnt match process 23080 exit code 1
---
---
---
---
---
---
---
---[/code]
<br/>
Thank you in advance!
View the full article