I have 2 functions that the main run them as a thread. first one reads from a file of messages and pushes every line into a queue and the second function writes into a file the messages of the queue.
from some reason, the queue in the second function doesn't update and the function writes nothing to the output file
here is the first func:
void MessagesSender::saveMessages()
{
std::string line = "";
std::ifstream file;
file.open(DATA_FILE);
if (file.is_open())
{
while (getline(file, line))
{
m.lock();
messages.push(line);
m.unlock();
}
file.close();
file.open(DATA_FILE, std::ofstream::out | std::ofstream::trunc);//clear file
file.close();
}
}
Here is the second:
void MessagesSender::sendMessages()
{
std::ofstream file(OUTPUT_FILE);
m.lock();
std::queue<std::string> temp_messages = messages;//problem - "messages" is empty
m.unlock();
if (file.is_open())
{
int i = 0;
while (!temp_messages.empty())
{
while (i < userName.size())
{
file << userName[i] << ": " << temp_messages.front() << std::endl;
i++;
}
temp_messages.pop();
i = 0;
}
file.close();
}
else std::cout << "Unable to open file";
}
int main()
{
MessagesSender mailingList;
while (mailingList.menu()){}//shows to the user the menu
std::thread t1(&MessagesSender::saveMessages, mailingList);
std::thread t2(&MessagesSender::sendMessages, mailingList);
t1.join();
t2.join();
}
why "messages" is empty and how can I fixe it?
Related
I have a Windows service written in C++ with the Win32 API. Before entering in "service" mode, the C++ program tries to read a configuration file specified as an absolute path. But the program cannot read it and exits. Some debugging leaves me to suspect that this is because of file ownership.
Question is, how can I modify the file ownership (preferably with a power-shell script) , so that the file can be read?
Here are the relevant parts
main program (exits, file cannot be read)
int main()
{
std::string cfg_file_name = config::get_config(config::comm_config_file);
if (cfg.read(cfg_file_name) < 0)
{
events::start_log(cfg.log_path, cfg.log_spdlog_level);
SPDLOG_CRITICAL("Cannot read: " + cfg_file_name);
return 1;
}
function get_config() uses some Win32 API calls to get the executable path (where the file is located) and concatenates it with the file name, to get an absolute path
std::string config::get_config(const std::string& config_name)
{
#ifdef _MSC_VER
std::string s = config::get_executable_path();
//this is done before log starts; it will be written to C:\Windows\System32 as a first log/debugging tool
std::ofstream ofs("comm.txt");
ofs << "GetModuleFileName: " << s << std::endl;
TCHAR buf[MAX_PATH];
GetCurrentDirectory(MAX_PATH, buf);
ofs << "GetCurrentDirectory: " << buf << std::endl;
//change the current directory of the process to be the executable path
if (SetCurrentDirectory(s.c_str()) == 0)
{
ofs << "SetCurrentDirectory" << std::endl;
}
GetCurrentDirectory(MAX_PATH, buf);
ofs << "GetCurrentDirectory: " << buf << std::endl;
s += config_name;
ofs.close();
return s;
#else
return config_name;
#endif
}
service is created by a power-shell script
sc.exe create _comm_ftp_server binPath= "$install_dir\ftp_server.exe" start= auto obj= "NT AUTHORITY\LocalService" password= " "
to debug it, I wrote a simple test service that writes a file and reads that same file, with no problem (so, a file can be read)
int main(int argc, char* argv[])
{
std::string path = config::get_executable_path();
cfg.log_path = path;
events::start_log(cfg.log_path, "trace", true);
//A service process has a SERVICE_TABLE_ENTRY structure for each service that it can start.
//The structure specifies the service name and a pointer to the service main function for that service.
//The main function of a service program calls the StartServiceCtrlDispatcher
//function to connect to the service control manager (SCM)
SERVICE_TABLE_ENTRY service_table[] =
{
{ (LPSTR)service_name, (LPSERVICE_MAIN_FUNCTION)service_main },
{ NULL, NULL }
};
if (StartServiceCtrlDispatcher(service_table))
{
return 0;
}
else
{
return 1;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//service_main
/////////////////////////////////////////////////////////////////////////////////////////////////////
void WINAPI service_main(DWORD argc, LPTSTR* argv)
{
service_handle = RegisterServiceCtrlHandler(service_name, service_handler);
if (service_handle == NULL)
{
return;
}
service_stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (service_stop_event == NULL)
{
return;
}
report_status(SERVICE_START_PENDING);
report_status(SERVICE_RUNNING);
SPDLOG_INFO("service running..." + std::to_string(current_state));
HANDLE thread_service = 0;
thread_service = CreateThread(NULL, 0, service_thread, NULL, 0, NULL);
WaitForSingleObject(thread_service, INFINITE);
/////////////////////////////////////////////////////////////////////////////////////////////////////
//service shutdown requested
/////////////////////////////////////////////////////////////////////////////////////////////////////
CloseHandle(thread_service);
report_status(SERVICE_STOP_PENDING);
SPDLOG_INFO("service stop pending..." + std::to_string(current_state));
CloseHandle(service_stop_event);
report_status(SERVICE_STOPPED);
SPDLOG_INFO("service stopped..." + std::to_string(current_state));
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//service_thread
/////////////////////////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI service_thread(LPVOID lpParam)
{
std::string path = cfg.log_path;
SPDLOG_INFO("service started in..." + cfg.log_path);
path += "\\test.txt";
size_t i = 0;
while (WaitForSingleObject(service_stop_event, 0) != WAIT_OBJECT_0)
{
write_txt_file(path, "writing...#" + std::to_string(i));
i++;
Sleep(10000);
read_txt_file(path);
}
return ERROR_SUCCESS;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//write_txt_file
/////////////////////////////////////////////////////////////////////////////////////////////////////
void write_txt_file(const std::string& file_name, const std::string& input)
{
FILE* f = fopen(file_name.c_str(), "a+");
fprintf(f, "%s\n", input.c_str());
fclose(f);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//read_txt_file
/////////////////////////////////////////////////////////////////////////////////////////////////////
void read_txt_file(const std::string& file_name)
{
std::ifstream ifs;
ifs.open(file_name);
if (!ifs.is_open())
{
SPDLOG_ERROR("Cannot open: " + file_name);
return;
}
std::string line;
while (std::getline(ifs, line))
{
SPDLOG_INFO("Line: " + line);
}
ifs.close();
}
Examining the file written by the test service in Windows explorer (Properties->Details) reveals a file owner as LOCAL_SERVICE
The file that must be read has owner "Administrators"
This leaves me to suspect that this is the problem. How can the file ownership be changed, or is there a way to create the service with privileges that can read any file ?
reference for SC.EXE Create
https://learn.microsoft.com/en-US/windows-server/administration/windows-commands/sc-create
To read the file, std::ifstream is used (default read only)
int config::config_t::read(const std::string& fname)
{
try
{
std::ifstream ifs(fname);
ifs >> configuration_json;
ifs.close();
from_json(configuration_json, *this);
}
catch (const std::exception& e)
{
SPDLOG_ERROR(e.what());
return -1;
}
return 0;
}
The read error was because the library JSON for modern C++
https://github.com/nlohmann/json
detects an error reading the last entry of this file because of the comma ","
{
"archive_path":"D:\\archive",
"test_comm_input_path":"D:\\test_comm_input_path",
}
in the reading function
int config::config_t::read(const std::string& fname)
{
//this is done before log starts; it will be written to the executable path as a first log/debugging tool in service mode
std::ofstream ofs("comm.txt");
try
{
std::ifstream ifs;
std::ios_base::iostate mask = ifs.exceptions() | std::ios::failbit;
ifs.exceptions(mask);
ifs.open(fname);
if (!ifs.is_open())
{
ofs << "open fail: " << fname << std::endl;
ofs.close();
return -1;
}
else
{
ofs << "open: " << fname << std::endl;
}
ifs >> configuration_json;
ifs.close();
from_json(configuration_json, *this);
ofs << "read: " << fname << std::endl;
ofs.close();
}
catch (const std::exception& e)
{
ofs << "json read: " << e.what() << std::endl;
ofs.close();
return -1;
}
return 0;
}
where
void from_json(const nlohmann::json& j, config::config_t& c)
{
if (j.contains("archive_path"))
{
j.at("archive_path").get_to(c.archive_path);
}
}
I have a multi-threaded application(OpenMP), in which each thread do some calculations and find the value of _pre.
Then a file with name "file_pre.txt" is opened and write the part of the string starting from index _sub and _end.
It should not happen that two threads having the same file name open the file for writing.
Hence the writing part I wrote in critical section but it's expensive because I would like multiple threads having different file name to be able to write in the respective files simultaneously, and only lock them out when they have same value in _pre (same file name) (the updating thread could wait for the other threads to finish).
Does anyone have a solution?
PART OF CODE:
ofstream outfile;
#pragma omp parallel for num_threads(8) schedule(static) private(it) shared(array)
for (it = 0; it < array.size(); ++it)
{
int _pre, _sub = 0, _end;
if(---)
{
_pre = xyz;
_end = abc;
ostringstream strCounter;
strCounter << _pre;
string result = "file" + strCounter.str() + ".txt";
#pragma omp critical(outfile)
{
outfile.open(result.c_str(), std::ios_base::app);
if (outfile.is_open())
{
for (int count = _sub; count < _end; count++)
{
outfile << buffer[count];
}
outfile << "\n";
outfile.close();
} else cout << "Unable to open file";
_sub = _abc;
}
}
else
{
_pre = _xyz_;
_end = _abc_;
ostringstream strCounter;
strCounter << _pre;
string result = "file" + strCounter.str() + ".txt";
#pragma omp critical(outfile)
{
outfile.open(result.c_str(), std::ios_base::app);
if (outfile.is_open())
{
for (int count = _sub; count < _end; count++)
{
outfile << buffer[count];
}
outfile << "\n";
outfile.close();
} else cout << "Unable to open file";
_sub = _abc;
}
}
}
I'm writing a program to read data from .txt file and using it in output .txt file. I'm using two threads; The first thread is for reading the data from the .txt file, and the second one is for writing it in the output file. I'm a beginner in the programming with mutex and condition_variable and for somehow my program handles exceptions... The exception is
abort() has been called.
These are the two threads methods:
void MessagesSender::readsData()
{
ifstream data;
data.open("data.txt");
string buffer;
bool toEmpty = false;
std::unique_lock<mutex> locker(mtx, std::defer_lock);
if (data.is_open())
{
while (std::getline(data, buffer)) //reads line to the buffer.
{
locker.lock();
this->messages.push(buffer); //push the message to the queue.
locker.unlock();
cond.notify_one();
}
data.close();
toEmpty = true;
}
else
{
cout << "Error opening file... " << endl;
}
if (toEmpty) //empty the data file.
{
ofstream emptyFile;
emptyFile.open("data.txt", ofstream::out | ofstream::trunc);
emptyFile.close();
}
}
void MessagesSender::sendsData()
{
ofstream output;
output.open("output.txt");
string tempString;
string tempMessage;
if (output.is_open())
{
std::unique_lock<mutex> locker(mtx, std::defer_lock);
locker.lock();
cond.wait(locker);
while (!(this->messages.empty()))
{
tempMessage = this->messages.front();
this->messages.pop();
locker.unlock();
for (std::vector<string>::iterator it = this->userNames.begin(); it != this->userNames.end(); ++it)
{
tempString = *it;
tempString += ": ";
tempString += tempMessage;
tempString += "\n";
output << tempString;
}
}
output.close();
}
else
{
cout << "Error opening file... " << endl;
}
}
Why is the program handling exception?
One possible error is that you are repeatedly unlocking the mutex in your while-loop, even though the mutex is not locked:
if (output.is_open())
{
std::unique_lock<mutex> locker(mtx, std::defer_lock);
locker.lock();
cond.wait(locker);
while (!(this->messages.empty()))
{
tempMessage = this->messages.front();
this->messages.pop();
// if multiple messages are in the queue, you unlock multiple times
// even though the mutex is not locked
locker.unlock();
for (std::vector<string>::iterator it = this->userNames.begin(); it != this->userNames.end(); ++it)
{
tempString = *it;
tempString += ": ";
tempString += tempMessage;
tempString += "\n";
output << tempString;
}
}
output.close();
}
According to unique_lock::unlock this throws a std::system_error
I have a vector of strings of 2 folder names vector <myClass> vec_fileNames; which I filled by reading from a fileNames.txt which contains 2 lines:
First
Second
ifstream inFile("c:/file names.txt");
if(!inFile)
{
cout << "File Not Found!\n";
inFile.close();
}
else
{
string line;
myClass class;
while (getline(inFile, line))
{
class.setFileName(line);
vec_fileNames.push_back(class);
}
So, at this point my vec_fileName[0].getFileName = First and vec_fileName[1].getFileName = second
Now I wanted to open files inside the folders who's names are in the vector in a loop so I did this:
for(int i = 0; i < vec_fileNames.size(); i++)
{
string fileName = vec_fileNames[i].getFileName();
ifstream inFile("C:/Program Folder\\" + fileName + "goalFile.txt");
if(!inFile)
{
cout << "File Not Found!\n";
inFile.close();
}
else
{
while (getline(inFile, line))
{
//do something
}
}
So far everything is good except for the file not being opened. Is this even something that can be done in c++ or is there an error in the way I'm opening the file?
I created the same folder structure as you have:
C:\
Program Folder
First
goalFile.txt
Second
goalFile.txt
And ran the following simple code. Node that I don't store the filenames in a class, but directly into a vector.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std; // I'm no fan of this, but you obviously used it.
void loadFileNames(vector<string>& vec_fileNames)
{
ifstream inFile("c:\\file names.txt");
if(!inFile.is_open())
{
cout << "File Not Found!\n";
return;
// inFile.close(); -- no need to close, it is not open!
}
else
{
string line;
while (getline(inFile, line))
{
cout << line << endl;
vec_fileNames.push_back(line);
}
}
}
void openFiles(vector<string>& vec_fileNames)
{
for(int i = 0; i < vec_fileNames.size(); i++)
{
string fileName = vec_fileNames[i];
string path("C:\\Program Folder\\" + fileName + "\\goalFile.txt");
ifstream inFile(path.c_str());
if(!inFile.is_open())
{
cout << "File" << vec_fileNames[i] << "Not Found!" << endl;
}
else
{
cout << "opened file in folder " << vec_fileNames[i] << endl << endl;
string line;
while (getline(inFile, line))
{
cout << line << endl;
}
cout << endl;
}
}
}
int main(int argc, char* argv[])
{
vector<string> fileNames;
loadFileNames(fileNames);
openFiles(fileNames);
return 0;
}
That works, and produces the output:
First
Second
opened file in folder First
First goal file 1
First goal file 2
opened file in folder Second
Second goalfile 1
Second goalfile 2
The lines First goal file 1, etc. are the contents of the two files.
I'm using the below code to create threads and add them to a thread pool. The threads load fine and each perform a simple looping routine until the main thread calls ResetWorkerThreads a second time and kills off the sub threads. The sub threads are interrupted however the main thread exits also. There are no errors written to console. I can't wrap my head around it as it doesn't appear to have any exception and the main thread has not been added to the vecThreads thread pool. Also the second time this function is all the "All Threads Killed" is not outputted as if it never reaches that point.
std::string strPreviousSettings = "0";
std::string strPreviousAgentSettings = "0";
bool boolResetWorkers;
std::string strIP;
std::string strMACAddress;
boost::thread_group vecThreads;
std::string GetIP()
{
std::string strIP;
try
{
using namespace boost::network;
std::string strRequest;
http::client client;
http::client::request request("http://test.com/ip.php");
http::client::response response = client.get(request);
strIP = body(response);
}
catch(...)
{
cout << "GetLocalIP - Error: " << endl;
}
return strIP;
}
std::string getMacAddress()
{
std::string strMACAddress = GetFileContents("/sys/class/net/eth0/address");
boost::replace_all(strMACAddress, ":", "");
boost::replace_all(strMACAddress, "\n", "");
return strMACAddress;
}
void ThreadSettingsWorker()
{
int x = 1;
strIP = GetIP();
strMACAddress = getMacAddress();
do {
CheckEventSettings();
CheckAgentSettings();
if(boolResetWorkers==true)
{
ResetWorkerThreads();
} else {
boost::this_thread::sleep(boost::posix_time::milliseconds(3000));
}
} while ( x != 0 );
}
void ResetWorkerThreads()
{
cout << "Resetting Workers Threads\n";
boolResetWorkers = false;
int intWorkerCount = 10; //Spawn 10 workers
int X = 0;
int intI = 1;
cout << "Kill All Threads\n";
try
{
vecThreads.interrupt_all();
}
catch(...)
{
//std::cerr << "Kill All Threads: " << std::endl;
}
cout << "All Threads Killed\n";
for (int i = 0; i < intWorkerCount; ++i)
{
cout << "Starting Worker: " << (i + 1) << "\n";
boost::thread tWorker(&ThreadWorker, (i + 1));
vecThreads.add_thread(&tWorker);
}
}
void TestRequest()
{
try
{
using namespace boost::network;
std::stringstream ss;
http::client client;
ss << "http://test.com/sadasdasd.html";
http::client::request request(ss.str());
http::client::response response = client.get(request);
std::string strOutput = body(response);
cout << "Test Request Out: " << strOutput << "\n";
}
catch(...)
{
cout << "TestRequest - Error: " << endl;
return;
}
}
void ThreadWorker(int intThread)
{
try
{
int X = 0;
do {
cout << "Thread " << intThread << "\n";
TestRequest();
} while ( X != 55 );
}
catch(...)
{
}
}
void CheckEventSettings()
{
try
{
using namespace boost::network;
std::string strRequest;
http::client client;
http::client::request request("http://test.com/events.php");
http::client::response response = client.get(request);
std::string strOutput = body(response);
if(strPreviousSettings==strOutput)
{
cout << "No Event Settings Changes\n";
} else {
cout << "Applying New Event Settings\n";
strPreviousSettings = strOutput;
std::string strDividerLine = "<br>";
std::string strDividerField = "<field>";
std::vector<std::string> vEvents;
vEvents = EasySplit(strOutput, strDividerLine);
for(std::vector<std::string>::const_iterator iEvent = vEvents.begin(); iEvent != vEvents.end() - 1; ++iEvent) {
}
}
}
catch(...)
{
cout << "CheckEventSettings - Error: " << endl;
return;
}
}
void CheckAgentSettings()
{
try
{
using namespace boost::network;
std::stringstream ss;
http::client client;
ss << "http://test.com/checksettings.php";
http::client::request request(ss.str());
http::client::response response = client.get(request);
std::string strOutput = body(response);
if(strPreviousAgentSettings==strOutput)
{
cout << "No Agent Settings Changes\n";
} else {
cout << "Applying New Agent Settings\n";
strPreviousAgentSettings = strOutput;
boolResetWorkers = true;
}
}
catch(...)
{
cout << "CheckAgentSettings - Error: " << endl;
return;
}
}
int main()
{
// Start thread
boost::thread tCheckSettings(&ThreadSettingsWorker);
// Ask thread to stop
//tCheckSettings.interrupt();
// Join - wait when thread actually exits
tCheckSettings.join();
return 0;
}
You have an error here:
boost::thread tWorker(&ThreadWorker, (i + 1));
vecThreads.add_thread(&tWorker);
You create a local object tWorker that is deleted just after call to add_thread(). So vecThreads contains the dangling pointers to threads. When you call vecThreads.interrupt_all() you get undefined behavior because vecThreads tries to access the deleted thread objects and I suppose your program just terminates because of access violation or something.
You have to change your code to something like this:
boost::thread* ptWorker = new boost::thread(&ThreadWorker, (i + 1));
vecThreads.add_thread(ptWorker);
Please note that you don't need to delete those thread objects yourself. thread_group will delete them itself.
ADDITION:
The problem with terminate() may be caused by destructor of http::client throwing an exception. Please try this to possibly eliminate that problem in TestRequest():
try{
http::client client;
try{
// other code
}
catch (){}
}
catch(){}
Also I'd suggest resetting vecThreads after interrupt_all(). For example you can define it as boost::scoped_ptr and then do pvecThreads.reset(new boost::thread_group()) after the call to interrupt_all().
At present the interrupted threads still remain in the thread_group after the interruption and then you try to interrupt them again along with the new threads added to the thread_group later in ResetWorkerThreads().
Inside of ResetWorkerThreads You have:
for (int i = 0; i < intWorkerCount; ++i)
{
cout << "Starting Worker: " << (i + 1) << "\n";
// One issue is here, don't create a thread on the stack
// and pass it to the thread group use new instead!
boost::thread tWorker(&ThreadWorker, (i + 1));
vecThreads.add_thread(&tWorker);
}
You are adding a thread created on the stack to the thread group. As soon as you iterate over the loop that threads memory is invalidated. You will need to new the thread and pass that pointer to add_thread.