mr.Cracker mr.Cracker - 2 months ago 14
C Question

Why do I get Access Violation in mutithreading aplication in C while I exit the main() before the threads finish?

I have one program, where i create 4 threads but only wait for the 1st thread[0].
The threads just print some contents. But when I do the below program I get Access violation sometimes.

#include<Windows.h>
#include <stdio.h>
#include<tchar.h>

#define KEYLEN 8
#define DATALEN 56
typedef struct _RECORD{
CHAR key[KEYLEN];
TCHAR data[DATALEN];
}RECORD;
#define RECSIZE sizeof(RECORD)
typedef RECORD *LPRECORD;

typedef struct _THREADARG{
DWORD iTh;
LPRECORD lowRecord; // pointer to Low record
LPRECORD highRecord;
} THREADARG, *PTHREADARG;

static int KeyCompare(LPCTSTR, LPCTSTR);
static DWORD WINAPI SortThread(PTHREADARG pThArg);
static DWORD nRec; // Total Number of records to be sorted
static HANDLE *pThreadHandle;


DWORD Options (int argc, LPCTSTR argv [], LPCTSTR OptStr, ...);

int _tmain(int argc, LPTSTR argv[]){
HANDLE hFile, mHandle;
LPRECORD pRecords = NULL;
DWORD lowRecordNum, nRecTh,numFiles, iTh;
LARGE_INTEGER fileSize;
BOOL noPrint;
int iFF, iNP;
PTHREADARG threadArg;
LPTSTR stringEnd;

iNP = 1;
iFF = iNP + 1;
numFiles = _ttoi(argv[iNP]);

if (argc <= iFF)
_tprintf (_T ("Usage: sortMT [options] nTh files."));

/* Open the file and map it */
hFile = CreateFile (argv[iFF], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
_tprintf (_T ("Failure to open input file."));

if (!SetFilePointer(hFile, 2, 0, FILE_END) || !SetEndOfFile(hFile))
_tprintf (_T ("Failure position extend input file."));

mHandle = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (NULL == mHandle)
_tprintf (_T ("Failure to create mapping handle on input file."));

/* Get the file size. */
if (!GetFileSizeEx (hFile, &fileSize))
_tprintf (_T ("Error getting file size."));

nRec = (DWORD)fileSize.QuadPart / RECSIZE;
nRecTh = nRec / numFiles;
threadArg = malloc (numFiles * sizeof (THREADARG)); /* Array of thread args. */
pThreadHandle = malloc (numFiles * sizeof (HANDLE));

/* Map the entire file */
pRecords = MapViewOfFile(mHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (NULL == pRecords)
_tprintf (_T ("Failure to map input file."));
CloseHandle (mHandle);

/* Create the sorting threads. */
lowRecordNum = 0;
for (iTh = 0; iTh < numFiles; iTh++) {
threadArg[iTh].iTh = iTh;
threadArg[iTh].lowRecord = pRecords + lowRecordNum;
threadArg[iTh].highRecord = pRecords + (lowRecordNum + nRecTh);
lowRecordNum += nRecTh;
pThreadHandle[iTh] = (HANDLE)_beginthreadex (
NULL, 0, SortThread, &threadArg[iTh], CREATE_SUSPENDED, NULL);
}

/* Resume all the initially suspened threads. */

for (iTh = 0; iTh < numFiles; iTh++)
ResumeThread (pThreadHandle[iTh]);


/* Wait for the sort-merge threads to complete. */

WaitForSingleObject (pThreadHandle[0], INFINITE);
for (iTh = 0; iTh < numFiles; iTh++);
CloseHandle (pThreadHandle[iTh]);

//Sleep(3); // Un comment it and see no error.


UnmapViewOfFile(pRecords);
// Restore the file length
/* SetFilePointer is convenient as it's a short addition from the file end */
if (!SetFilePointer(hFile, -2, 0, FILE_END) || !SetEndOfFile(hFile))
_tprintf("Failure restore input file lenght.");

CloseHandle(hFile);
free (threadArg); free (pThreadHandle);
return 0;
}


DWORD WINAPI SortThread(PTHREADARG pThArg){

DWORD groupSize = 2, myNumber, twoToI = 1;
/* twoToI = 2^i, where i is the merge step number. */
DWORD_PTR numbersInGroup;
LPRECORD first;

myNumber = pThArg->iTh;
first = pThArg->lowRecord;
numbersInGroup = (DWORD)(pThArg->highRecord - first);

printf(" %d : %s \n",myNumber, pThArg->lowRecord );

return 0;
}


file content structure:

02d2d159. Record Number: 00000000.abcdefghijklmnopqrstuvwxyz X
f362711e. Record Number: 00000001.abcdefghijklmnopqrstuvwxyz X
ba957dff. Record Number: 00000002.abcdefghijklmnopqrstuvwxyz X


But if I give a Sleep(N); after the threads creation there is no problem.!
So I understood the main() thread exits before the child threads. But when main() exits entire process should terminate. Isn't it?
Then how does the Access Violation possible?

why do I Access violation?

Answer

But when main() exits entire process should terminate.

Ultimately yes, but between return from main and actual process termination may actually be a lot of code running, tearing down the process image etc.

Thus, when you ...

free (threadArg); free (pThreadHandle);

... and one or more threads haven't yet completed and will ...

printf(" %d : %s \n",myNumber,  pThArg->lowRecord );

... access for example their thread argument pThArg (which is now deleted) you've signed up for trouble.

Note that this is just an example. There's probably much more (internal) shared data that is freed after return from main but still used by the threads (their stacks for example, thread handles, ...).

Moral: Wait for all threads that use a resource to terminate before freeing that resource.


Also: pThArg->lowRecord is not a char * as expected by %s, thus you have undefined behavior there, too.

Comments