I don't have enough reputation points to comment to ask if they solved the problem originally stated here. I have the same problem of the program crashing in construction of an ofstream.
It is not multi-threaded access, but it can be called in quick succession. I believe it crashes on the 2nd time. I use scope to ensure the stream object is destroyed.
Why would this happen?
I tried std::fopen too. It also results in a crash.
Here is the code using the ofstream:
static bool writeConfigFile (const char * filename, const Config & cfg)
{
logsPrintLine(_SLOG_SETCODE(_SLOGC_CONFIG, 0), _SLOG_INFO, "Write config file (%s stream)", filename);
ofstream os(filename); // FIXME: Crashes here creating ofstream 2nd time
if (os.good())
{
// Uses stream insertion operator to output attributes to stream
// in human readable form (about 2kb)
outputConfig(cfg, os);
if (!os.good())
{
logsPrintLine(_SLOG_SETCODE(_SLOGC_CONFIG, 0), _SLOG_NOTICE, "Failed to write configuration file (%s)", filename);
return false;
}
logsPrintLine(_SLOG_SETCODE(_SLOGC_CONFIG, 0), _SLOG_INFO, "Configuration written to file (%s)", filename);
return true;
}
logsPrintLine(_SLOG_SETCODE(_SLOGC_CONFIG, 0), _SLOG_NOTICE, "Cannot write configuration file (%s)", filename);
return false;
}
/**
* Called when configuration settings have been read/received and validated
* #return true if successfully set, and written to file
*/
bool Config::set (SysConfigSource source, const struct SCADA_dsconfig * p)
{
Lock lock(mtxSet); // This is locking a mutex on construction of the lock. Release it on destruction.
// Setup the non-current one to switch to
Config * pCfg = pConfig.other();
unsigned i, f, n = 0;
// set attributes in pCfg based on the config received
// and some constants ...
pCfg->setWritten(writeConfigFile("test.conf", *pCfg));
if (!pCfg->isWritten())
{
// Don't set system config status here. Existing one still in use.
logsPrintLine(_SLOG_SETCODE(_SLOGC_CONFIG, 0), _SLOG_NOTICE, "Config file not written. Retain prior config.");
return false;
}
pConfig.swap(); // switch-in the new config
setSystemConfigSource(source);
toSyslog(pCfg);
notifyConfigChange();
return true;
}
Maybe post a segment of your source code in order to get an idea of where it went wrong.
Here is a very basic segment of code of how I would use fstream.. hope you will find it helpful.
#include <iostream>
#include <fstream>
#include <string>
int main() {
while (1) {
std::string testString;
std::ofstream outFile;
outFile.open("Test", std::ios_base::app); // Appends to file, does not delete existing code
std::cout << "Enter a string: ";
std::cin >> testString;
outFile << testString << std::endl;
outFile.close();
}
}
It turned out to be a device driver bus master issue. Add "ahci nobmstr" when launching devb-ahci.
Derived via http://www.qnx.com/developers/docs/qnxcar2/index.jsp?topic=%2Fcom.qnx.doc.neutrino.user_guide%2Ftopic%2Fhardware_Troubleshooting_devb-eide.html
Related
I have written a function to check whether a file on disk is already in use. This is to avoid trying to execute a freshly downloaded installer while the antivirus is checking it, which fails.
The (generic) function looks like that:
bool isFileInUse(const QString& filePath)
{
QFile f(filePath);
if (!f.exists())
{
qDebug() << "File does not exist:" << filePath;
return false;
}
if (!f.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::ExistingOnly))
{
qDebug() << "File in use:" << filePath;
return true;
}
else
{
f.close();
qDebug() << "File free:" << filePath;
return false;
}
}
This works, I have tested manually with an installer (.exe) and it returns the expected result.
But now I want to write a unit test to check that function.
I have tried to create a file, and open it with QFile::open(QIODevice::WriteOnly), then call isFileInUse(..) on it, expecting to be already "in use", but it always returns false, i.e. Qt seem to have no problem to open twice the same file even in WriteOnly !
TEST(FilesUtils, isFileInUse)
{
QTemporaryDir dir;
const QString filePath = dir.filePath("test.txt");
createFile(filePath); // open, write dummy data and close the file
EXPECT_FALSE(FilesUtils::isFileInUse(filePath));
QFile f(filePath);
EXPECT_TRUE(f.open(QIODevice::WriteOnly | QIODevice::Append)); // OK
EXPECT_TRUE(FilesUtils::isFileInUse(filePath)); // FAILS, returns false
}
I have tried to open the file with a software like notepad.exe, and it also returns false.
Then I tried with Microsoft Word, and there it returns finally true (= file in use). But this is not portable and I cant expect a user to have Word installed on Windows, obviously.
Is there any way to open a file with Qt such that another QFile::open() returns false ? (i.e. lock the file)
Or does anyone sees something wrong in the code above ?
On Windows a file is opened for reading and/or writing and a share mode allowing additional reading/writing/deleting. This can create many interesting combinations, for example a file can be open for writing and allow additional open for reading but not writing. Or a file can be open for reading and allowing renames/deletes. See dwShareMode parameter of CreateFile WinAPI.
Unfortunately QFile::open API doesn't support share mode, since Qt is a portable framework and share mode exists only on Windows.
See these links with additional information for possible alternative solutions:
QLockFile
Qt: How to lock/prevent a file from being read while it is written?
Is possible to set QFile share permission?
Locking files using CreateFile on Windows
Once your target file has been created and opened, you should use the open() method with the QIODevice::NewOnly flag if it is to be called again.
QIODevice::NewOnly | 0x0040 | Fail if the file to be opened already exists. Create and open the file only if it does not exist. There is a guarantee from the operating system that you are the only one creating and opening the file. Note that this mode implies WriteOnly, and combining it with ReadWrite is allowed. This flag currently only affects QFile. Other classes might use this flag in the future, but until then using this flag with any classes other than QFile may result in undefined behavior. (since Qt 5.11)
Alternatively you could use QFile::isOpen() to test for prior file opening in function IsFileInUse:
if (f.isOpen()) return true;
Below is the code that proves the point (adapted from OP, which does not run out of the box):
#include <QString>
#include <QFile>
#include <QTemporaryDir>
#include <QtDebug>
#include <iostream>
void createFile(const QString& filePath)
{
// open, write dummy data and close the file
QFile f(filePath);
f.open(QIODevice::WriteOnly | QIODevice::Append);
f.write("dummy");
f.close();
}
#define EXPECT_FALSE(X) std::cerr << (X == false ? "OK, FALSE" : "NOT FALSE!") << std::endl;
#define EXPECT_TRUE(X) std::cerr << (X == true ? "OK, TRUE" : "NOT TRUE!") << std::endl;
class FilesUtils {
public:
static bool isFileInUse(const QString& filePath)
{
QFile f(filePath);
if (!f.exists())
{
qDebug() << "File does not exist:" << filePath;
return false;
}
if (!f.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::ExistingOnly))
{
qDebug() << "File in use:" << filePath;
return true;
}
else
{
f.close();
qDebug() << "File free:" << filePath;
return false;
}
}
};
int main()
{
QTemporaryDir dir;
const QString filePath = dir.filePath("test.txt");
if (QFileInfo(filePath).exists()) QFile(filePath).remove();
createFile(filePath); // open, write dummy data and close the file
EXPECT_FALSE(FilesUtils::isFileInUse(filePath));
QFile f(filePath);
EXPECT_TRUE(f.open(QIODevice::WriteOnly | QIODevice::Append)); // OK, returns true
EXPECT_TRUE(FilesUtils::isFileInUse(filePath)); // FAILS, returns false
f.close();
EXPECT_FALSE(f.open(QIODevice::WriteOnly | QIODevice::Append| QIODevice::NewOnly)); //OK, returns false
EXPECT_FALSE(FilesUtils::isFileInUse(filePath)); // SUCCEEDS, returns false
}
This code runs as expected:
File free: "/tmp/qt_temp-zCTbRf/test.txt"
OK, FALSE
OK, TRUE
File free: "/tmp/qt_temp-zCTbRf/test.txt"
NOT TRUE!
OK, FALSE
File free: "/tmp/qt_temp-zCTbRf/test.txt"
OK, FALSE
So, I get infinite cycle while trying to read lines from file (line by line). I was trying to use do{}while(); cycle like that:
QTextStream stream(stdin);
QString line;
do {
line = stream.readLine();
} while (!line.isNull());
but I get empty string.
Sure, I checked file path (it is right). I was trying to use /Users/user/tts.txt path but without changes. I was trying to read other files (like m3u). And it's not working on macOS Catalina, Windows 10, Linux (Debian).
So, why did I get infinite cycle?
QStringList Manager::GetLinesFromFile(const QString &nameOfFile)
{
QStringList lines = {};
//path to file
const QString path = QCoreApplication::applicationDirPath() + "/bin/" + "tts.txt";
//"/Users/user/tts.txt"
QFile buffer;
buffer.QFile::setFileName(path);
#ifndef Q_DEBUG
qDebug() << path;
#endif
if(buffer.QFile::exists())
{
if(!buffer.QIODevice::open(QIODevice::ReadOnly))
{
#ifndef Q_DEBUG
qCritical() << "error: can't open file";
#endif
}
else
{
QTextStream stream(&buffer);
// both conditions
// (!stream.QTextStream::atEnd())
while(!buffer.QFileDevice::atEnd())
lines.QList::push_back(stream.QTextStream::readLine());
buffer.QFile::close();
}
}
else
{
#ifndef Q_DEBUG
qCritical() << "error: file not exists";
#endif
}
return lines;
}
Have a look at the QTextstream documentation https://doc.qt.io/qt-5/qtextstream.html. There is an example of reading line by line. Your while loop should read until the stream reaches the end of the buffer and many of the in built read functions will return false when his happens
So, I got it. I opened the file incorrectly.
I was using:
if(!file.QIODevice::open(QIODevice::ReadOnly))
but it should be like that:
if(!file.QFile::open(QFile::ReadOnly))
I appreciate any guidance or help I can get on this. I am writing a program with values for a PID stored on an SD card so I can change them on the touchscreen without the need to hook it up to my computer. I want a single function that I can call with parameters allowing me to increase or decrease the number and change the file name. The below function is what I have to change whether "pnum.txt" increases or decreases; however I cannot figure out how to make a File work as a parameter.
I have tried making " "pnum.txt" " (with quotes) as a char and as a String, and even though it prints out as it should, it doesn't work when inserted into the function. I have also tried passing the whole SD.open("pnum.txt", FILE_WRITE) and myFile = SD.open("pnum.txt", FILE_WRITE) as a File, but that does something odd - it will create the file when I open it, but it won't write to the file. I'm finding myself just trying the same things over and over, so I clearly have a lack of understanding that I'm not finding anywhere. Thank you again for any help on this!
float incDecValue(float value) {
//This is important, because the libraries are sharing pins
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
value;
SD.remove("pnum.txt");
myFile = SD.open("pnum.txt", FILE_WRITE);
myFile.print(value);
myFile.close();
counter = 0;
myFile = SD.open("pnum.txt");
if (myFile) {
while (myFile.available()) {
testChar[counter] = myFile.read();
counter++;
}
myFile.close();
}
float convertedValue = atof(testChar);
return convertedValue;
}
And I will call it like this.
pValue = incValue(pValue+=.5);
As not exactly knowing what you really want I did the following assumptions:
You want to save a single float value to a file called pnum.txt
You want to do something with that value
You want the processed value to write back to the file pnum.txt (overwriting the content)
Two different functions parametrized each with fileName as input and the value for write
So here a complete sequence (works as posted) you could easily implement into your code. No String class is used and its a one line file read/write only:
/* SD card pid read/write
The circuit:
SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4 */
#include <SPI.h>
#include <SD.h>
const uint8_t chipSelect = 4;
char dataChar[16] = {'\0'};
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect.
}
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed or no card present");
return;
}
Serial.println("Card initialized");
}
void loop() {
float pValue = readFileValue ("pnum.txt") + 0.5;
writeFileValue ("pnum.txt", pValue);
}
float readFileValue (const char* fileName) {
/* Open the file. note that only one file can be open at a time,
so you have to close this one before opening another.*/
File dataFile = SD.open(fileName, FILE_READ);
// if the file is available, write to it:
if (dataFile) {
char c;
uint8_t i = 0;
while (dataFile.available()) {
c = dataFile.read(); // Read char by char
if (c != '\n') { // As long no line terminator
dataChar[i] = c;
i++;
}
else {
dataChar[i] = '\0'; // Terminate char array properly
dataFile.close();
break;
}
}
Serial.print("Success writing content: ");
Serial.println(dataChar);
}
else { // If the file isn't open, pop up an error:
Serial.print("Error opening requested file: ");
Serial.println(fileName);
}
float fileVal = atof(dataChar);;
return fileVal;
}
bool writeFileValue (const char* fileName, float fileVal) {
SD.remove(fileName); // Delete the existing file if existing
File dataFile = SD.open(fileName, FILE_WRITE);
// If the file opened okay, write to it
if (dataFile) {
// dtostrf(floatvar, StringLengthIncDecimalPoint, numVarsAfterDecimal, charbuf);
dtostrf(fileVal, 5, 2, dataChar); // Not really needed for this simple issue, but ..
Serial.print("Writing to file: ");
Serial.println(fileName);
dataFile.println(dataChar);
// Close the file
dataFile.close();
Serial.println("Success saving done");
return true;
} else {
// if the file didn't open, print an error:
Serial.println("Error opening file: ");
Serial.println(fileName);
return false;
}
}
I have a Qt console application. All communication with the user goes over two streams:
QTextStream in(stdin);
QTextStream out(stdout);
Now I want to log the whole session to a file, but I don't want to add a log output on every location where the streams are used.
Is there an easy way to "tee" the data of both streams to the console and to a file?
The app has to run on Windows.
I suggest a very simple solution, but c++14 is recommended.
I would wrap QTextStream this way (output only as example):
#include <QTextStream>
#include <QFile>
class OutTeeTextStream
{
public:
OutTeeTextStream()
{
logfilestream = 0;
stdoutstream = new QTextStream(stdout);
file.setFileName("/tmp/outlog.log");
if(file.open(QFile::Append))
{
logfilestream = new QTextStream(&file);
}
}
~OutTeeTextStream()
{
file.close();
delete stdoutstream;
delete logfilestream;
}
OutTeeTextStream &operator<<(auto data)
{
(*stdoutstream) << data;
if(logfilestream != 0)
{
(*logfilestream) << data;
}
return (*this);
}
private:
QTextStream * stdoutstream;
QTextStream * logfilestream;
QFile file;
};
As you can see, the wrapper holds two QTextStream instances and provides a stream insertion operator, which basically broadcasts data to each of them.
Not using c++14 (e.g. c++11) will result in complaints about use of ‘auto’ in parameter declaration. In this case, one had to declare/define every insertion (and extraction) operator overload (about fifteen / seventeen in QTextStream).
Refactoring would consist of replacing every QTextStream(stdout) with OutTeeTextStream().
In C++ is there any way to make the writing into file thread safe in the following scenario ?
void foo_one(){
lock(mutex1);
//open file abc.txt
//write into file
//close file
unlock(mutex1);
}
void foo_two(){
lock(mutex2);
//open file abc.txt
//write into file
//close file
unlock(mutex2);
}
In my application (multi-threaded) , it is likely that foo_one() and foo_two() are executed by two different threads at the same time .
Is there any way to make the above thread safe ?
I have considered using the file-lock ( fcntl and/or lockf ) but not sure how to use them because fopen() has been used in the application ( performance reasons ) , and it was stated somewhere that those file locks should not be used with fopen ( because it is buffered )
PS : The functions foo_one() and foo_two() are in two different classes , and there is no way to have a shared data between them :( , and sadly the design is such that one function cannot call other function .
Add a function for logging.
Both functions call the logging function (which does the appropriate locking).
mutex logMutex;
void log(std::string const& msg)
{
RAIILock lock(logMutex);
// open("abc.txt");
// write msg
// close
}
If you really need a logger, do not try doing it simply by writing into files and perhaps use a dedicated logger, thus separating the concerns away from the code you're writing. There's a number of thread-safe loggers: the first one that comes to mind: g2log. Googling further you'll find log4cplus, a discussion here, even a minimalist one, +1
If the essence of functions foo_one() and foo_two() are only to open the file, write something to it, and close it, then use the same mutex to keep them from messing each other up:
void foo_one(){
lock(foo_mutex);
//open file abc.txt
//write into file
//close file
unlock(foo_mutex);
}
void foo_two(){
lock(foo_mutex);
//open file abc.txt
//write into file
//close file
unlock(foo_mutex);
}
Of course, this assumes these are the only writers. If other threads or processes write to the file, a lock file might be a good idea.
You should do this, have a struct with a mutex and a ofstream:
struct parser {
ofstream myfile
mutex lock
};
Then you can pass this struct (a) to foo1 and foo2 as a void*
parser * a = new parser();
initialise the mutex lock, then you can pass the struct to both the functions.
void foo_one(void * a){
parser * b = reinterperet_cast<parser *>(a);
lock(b->lock);
b->myfile.open("abc.txt");
//write into file
b->myfile.close();
unlock(b->mutex);
}
You can do the same for the foo_two function. This will provide a thread safe means to write to the same file.
Try this code. I've done this with MFC Console Application
#include "stdafx.h"
#include <mutex>
CWinApp theApp;
using namespace std;
const int size_ = 100; //thread array size
std::mutex mymutex;
void printRailLock(int id) {
printf("#ID :%", id);
lock_guard<std::mutex> lk(mymutex); // <- this is the lock
CStdioFile lastLog;
CString logfiledb{ "_FILE_2.txt" };
CString str;
str.Format(L"%d\n", id);
bool opend = lastLog.Open(logfiledb, CFile::modeCreate | CFile::modeReadWrite | CFile::modeNoTruncate);
if (opend) {
lastLog.SeekToEnd();
lastLog.WriteString(str);
lastLog.Flush();
lastLog.Close();
}
}
int main()
{
int nRetCode = 0;
HMODULE hModule = ::GetModuleHandle(nullptr);
if (hModule != nullptr)
{
if (!AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0))
{
wprintf(L"Fatal Error: MFC initialization failed\n");
nRetCode = 1;
}
else
{
std::thread threads[size_];
for (int i = 0; i < size_; ++i) {
threads[i] = std::thread(printRailLock, i + 1);
Sleep(1000);
}
for (auto& th : threads) { th.hardware_concurrency(); th.join(); }
}
}
else
{
wprintf(L"Fatal Error: GetModuleHandle failed\n");
nRetCode = 1;
}
return nRetCode;
}
Referance:
http://www.cplusplus.com/reference/mutex/lock_guard/
http://www.cplusplus.com/reference/mutex/mutex/
http://devoptions.blogspot.com/2016/07/multi-threaded-file-writer-in-c_14.html