Write a multithreaded C++ application (executable) for Windows Platform. This program uses the two threads to read and process the input data and store it into an output file. Candidate is free to make it console or UI based Windows application. Detailed description
- Program should accept an input text file with HTML content (xxx.html).
- ‘Thread1’ reads the input file and stores the data into a ‘buffer’. As soon as ‘buffer’ is full, it pauses and waits for some of the data to be processed by ‘Thread 2’ and then continues again. ‘Thread1’ never waits for ‘buffer’ to be completely empty. As soon as even few bytes space becomes available, it starts filling it again.
- As soon as data starts getting stored in ‘buffer’, ‘Thread2’ starts reading it. It removes the html tags associated with text data and pushes the text only data to ‘output_file.txt’ with total text words count as summary at the end of output file.
HTMLReader.h
#pragma once
#ifndef _HTMLReader_HEADER_
#define _HTMLReader_HEADER_
#include "Common.h"
class HTMLReader
{
public:
HTMLReader();
~HTMLReader(void);
HANDLE startReading(ThreadObject*);
static queue<unsigned char> buffer;
private:
static const size_t buffer_size;
static bool isBufferFull();
static DWORD __stdcall FetchHTML(LPVOID params);
};
#endif _HTMLReader_HEADER_
HTMLReader.cpp
#include "HTMLReader.h"
#include <istream>
//initilize static members
const size_t HTMLReader::buffer_size = 1024;
queue<unsigned char> HTMLReader::buffer;
HTMLReader::HTMLReader()
{
}
HTMLReader::~HTMLReader(void)
{
}
//starts a thread which reads HTML file
HANDLE HTMLReader::startReading(ThreadObject* threadObject)
{
HANDLE handle = CreateThread(NULL,NULL,HTMLReader::FetchHTML,threadObject,0,NULL);
return handle;
}
//buffer is restricted to a size
bool HTMLReader::isBufferFull()
{
return HTMLReader::buffer.size() >= HTMLReader::buffer_size;
}
//thread function,
DWORD __stdcall HTMLReader::FetchHTML(LPVOID params)
{
ThreadObject threadObject = *(ThreadObject*)params;
FILE* fp = fopen(threadObject.data.c_str(),"r");
if(!fp)
{
cout<<"Error reading file."<<std::endl;
return -1;
}
//keep reading file till end of file
while(1)
{
//if buffer is full wait for reader to make space by poping from queue
while(HTMLReader::isBufferFull())
{
Sleep(100);
}
char ch = -1;
//take lock and add to queue till buffer is full
if(TryEnterCriticalSection(&threadObject.criticlaSection))
{
while(!(HTMLReader::isBufferFull()) && (ch = fgetc(fp)) !=EOF)
{
HTMLReader::buffer.push(ch);
}
LeaveCriticalSection(&threadObject.criticlaSection);
}
//if fp reaches eof then file reading is done
if(ch == EOF) break;
}
return 0;
}
HTMLParser.cpp (which contains main
)
#include "Common.h"
#include "HTMLParser.h"
#include "HTMLReader.h"
#include <fstream>
#include <vector>
static std::vector<unsigned char> data;
//thread function which reads data and from queue and adds to buffer
DWORD __stdcall getData(LPVOID params)
{
ThreadObject threadObject = *(ThreadObject*)params;
while(1)
{
//retry 10 times, if still does not get data then kill the thread
int retry_count = 0;
while(HTMLReader::buffer.empty())
{
Sleep(100);
retry_count++;
if(retry_count >= 10) return 0;
}
if(TryEnterCriticalSection(&threadObject.criticlaSection))
{
Sleep(10);
//read all the data in the buffer and then leave critical section
while(!HTMLReader::buffer.empty())
{
data.push_back(HTMLReader::buffer.front());
HTMLReader::buffer.pop();
}
LeaveCriticalSection(&threadObject.criticlaSection);
}
}
return 0;
}
int main(int argc, char* argv[], char* envp[])
{
string inputFilePath;
if(argc == 2)
inputFilePath += string(argv[1]); //get input from command line
CRITICAL_SECTION criticalSection;
ThreadObject threadObject;
InitializeCriticalSection(&criticalSection); //initilize critical secion
threadObject.criticlaSection = criticalSection; //fill thead object
if(inputFilePath.empty())
threadObject.data = "C:/assn/test.html"; //default path if user does not provide path
else
threadObject.data = inputFilePath;
HTMLReader htmlReader;
HANDLE fileReaderThread = htmlReader.startReading((ThreadObject*)&threadObject); // start reader therad
HANDLE dataStoreThread = CreateThread(NULL,NULL,getData,(ThreadObject*)&threadObject,0,NULL); //start parser thread
if(fileReaderThread) WaitForSingleObject(fileReaderThread,INFINITE); //wait for thread to return
if(dataStoreThread) WaitForSingleObject(dataStoreThread,INFINITE); //wait for thread to return
std::vector<unsigned char> finalOutput;
std::vector<unsigned char> :: const_iterator it = data.begin();
//process html data, remove html tags and store in file
while(it!=data.end())
{
while(*it == '<') //there can be <, immediately after >, so checkin in while
{
while(++it != data.end() && *it != '>');
it++;
}
if(it != data.end())
finalOutput.push_back(*it++);
if(it == data.end())
break;
}
it = finalOutput.begin();
std::ofstream out("output_file.txt");
int word_cont = 0;
string output;
for(; it!=finalOutput.end(); it++)
{
if(*it == '\n' || *it =='\t')
continue;
if(*it == ' ')
word_cont++;
output.push_back(*it);
}
word_cont++;
output += "\n\n\nSummary:\n";
output += "Word Count = ";
output += std::to_string(word_cont) + string("\n");
cout<<output;
out<<output;
out.close();
return 0;
}
Common.h
#ifndef _COMMON_HEADER_
#define _COMMON_HEADER_
#include "stdafx.h"
#include <vector>
#include <queue>
#include <string>
using std::vector;
using std::queue;
using std::string;
using std::cout;
struct ThreadObject
{
CRITICAL_SECTION criticlaSection;
string data;
};
#endif