Following thread code runs 2058 times, after that it crashes. Can somebody help me figure out why? The idea of the program is create some class in main thread, pass it to worker thread, thread fills needed data and pass data back to main thread. This example crashes after 2058 runs, however it should go indefinately. I've run it 20 times, always the same number. In version of reduced qWarning() calls (print simple line each 100 runs) thread gets executed 3000 times. So I guess it does not depend on amount of qWarning() calls. And why pointer address for SharedData *d is always the same?
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestThread* thread = new TestThread();
MainWindow w(thread);
w.show();
delete thread;
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui/QMainWindow>
#include <QThread>
#include <QHash>
class SharedData
{
public:
SharedData();
QString var;
QHash<QString, QString> hash;
};
class TestThread : public QThread
{
Q_OBJECT
public:
TestThread(QObject *parent = 0);
void doWork(SharedData* _data);
void doCrash(QHash<QString, QString>* hash);
signals:
void completed(SharedData* d);
private:
SharedData* data;
protected:
void run();
};
namespace Ui
{
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(TestThread* t, QWidget *parent = 0);
~MainWindow();
void runThread();
public slots:
void jobDone(SharedData* req);
private:
Ui::MainWindow *ui;
TestThread* t;
int runcount;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug.h>
TestThread::TestThread(QObject *parent) : QThread(parent)
{
}
void TestThread::run()
{
qWarning() << "Thread running";
data->var = "hello";
doCrash(&data->hash);
emit completed(data);
}
void TestThread::doWork(SharedData* _data)
{
data = _data;
qWarning() << "Attempting to start";
if(!isRunning())
{
run();
}
else
{
qWarning() << "Oops. Already running";
}
}
void TestThread::doCrash(QHash<QString, QString>* hash)
{
hash->insert("test", "123");
/*
QHashIterator<QString, QString> i(*hash);
while (i.hasNext()) {
i.next();
qWarning() << i.key() + ":" + i.value();
}
*/
}
SharedData::SharedData()
{
}
void MainWindow::jobDone(SharedData* req)
{
qWarning() << "RETURNED";
qWarning() << "var: " << req->var << " addr: " << &req->var;
qWarning() << "cnt: " << req->hash.count() << " addr: " << &req->hash;
QHashIterator<QString, QString> i(req->hash);
while (i.hasNext()) {
i.next();
qWarning() << i.key() + ":" + i.value();
}
delete req;
runThread();
}
MainWindow::MainWindow(TestThread* _t, QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
t = _t;
connect(t, SIGNAL(completed(SharedData*)), this, SLOT(jobDone(SharedData*)));
runcount = 0;
runThread();
}
void MainWindow::runThread()
{
SharedData* d = new SharedData();
d->var = "test";
runcount++;
qWarning() << "Run count: " << runcount;
qWarning() << "CREATING THREAD";
qWarning() << "var: " << d->var << " addr: " << &d->var;
qWarning() << "cnt: " << d->hash.count() << " addr: " << &d->hash;
t->doWork(d);
}
MainWindow::~MainWindow()
{
delete ui;
}
You shouldn't deleting your TestThread instance in main.cpp (comment out "delete thread;" string) !
As vnm has pointed out, the cause of the crash is most likely the delete thread; instruction in main.cpp: when you call w.show() it will return immediately, it is the exec() call that will start the event loop and block, but by then it is too late as the thread has already been deleted.
I would declare the thread as a non-pointer member of the main window instead of passing it as a parameter, this way the compiler will do the cleanup and initialization for you.
An even simpler solution would be to use QtConcurrent::run. By doing this you will eliminate all the explicit treading code, but still get the benefits of multi threading.
Related
Well, I have created a program which picks up the input signal from serial input. I can successfully receive the data transmitted from the device through UART. I want to terminate the thread after achieving certain conditions( such as receiving more than 5 bytes, etc.) I think the problem is how to terminate the thread in Qt correctly, but I couldn't find the way. The program seems falls into deadlock after calling the exec() in the sub function. Can anyone help with that problem? Thank you very much!
Here's my header file:
#ifndef SERIALTHREAD
#define SERIALTHREAD
#include <QtSerialPort/QSerialPort>
#include <QDebug>
#include <QString>
#include <QThread>
#include <QtCore>
#include <iostream>
#include <fstream>
class SerialControlThread : public QThread
{
Q_OBJECT
public:
explicit SerialControlThread(QString ComPort,QObject *parent = 0);
~SerialControlThread(); // Destructor
bool openSerialPort();
void closeSerialPort();
void run();
bool TelltoExit();
void StarttoRun();
private:
int DataCount;
QString ComPortNumber;
QSerialPort *serial;
int* VoltageStorage; // Total 3 channels, each channel takes 10 data
unsigned int Channel_A[10]; // Channel_A is for Phase Tx s
int DataCountIndexA; // This is how many data has been sent to the buffer;
int SentDataCount;
unsigned char StoreDataBuffer[2];
unsigned char TotalDataCounter;
std::ofstream write;
signals:
void BufferisFull(int*);
void TimeToQuit();
public slots:
private slots:
void readData();
void handleError(QSerialPort::SerialPortError error);
};
#endif // SERIALTHREAD
This is the.cpp
#include "serialcontrol.h"
#include <iostream>
SerialControlThread::SerialControlThread(QString ComPort,QObject *parent) :
QThread(parent),ComPortNumber(ComPort)
{
DataCountIndexA=0;
DataCount=0;
serial = new QSerialPort(this);
connect(this,SIGNAL(TimeToQuit()),this,SLOT(quit()));\
connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));
connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this,
SLOT(handleError(QSerialPort::SerialPortError)));
for (int i=0;i<10;i++)
Channel_A[i]=0;
}
SerialControlThread::~SerialControlThread()
{
this->closeSerialPort();
delete serial;
}
bool SerialControlThread::openSerialPort()
{
// std::cout << "Hey I am in serial function" << std::endl;
serial->setPortName(ComPortNumber) ;
serial->setBaudRate(QSerialPort::Baud9600); //This can be set through menu in the future
serial->setDataBits(QSerialPort::Data8); // A packets contains 8 bits ( 3 for signature bits)
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
if (!(serial->open(QIODevice::ReadWrite))) {
return false; // return false when the device can't be opened
}else
{
return true;} // return true when the device is avalaible
}
void SerialControlThread::closeSerialPort()
{
if (serial->isOpen())
serial->close();
}
void SerialControlThread::handleError(QSerialPort::SerialPortError error)
{
}
void SerialControlThread::readData()
{
QByteArray data=serial->read(100);
const char *TempChar=data.data();
std::cout << TempChar << std::endl;
DataCount++;
if(DataCount>=4)
{
std::cout << "I am bigger than 4" << std::endl;
this->quit();
}
}
}
void SerialControlThread::run()
{
}
bool SerialControlThread::TelltoExit()
{
}
void SerialControlThread::StarttoRun()
{
// Sending the msp430 S to activate the following sequence
const char *temp="S";
serial->write(temp);
serial->waitForBytesWritten(30000);
this->exec();
}
This is the main.cpp
#include <QCoreApplication>
#include <QtSerialPort/QSerialPortInfo>
#include <QList>
#include <iostream>
#include <QString>
#include <QDebug>
#include <QSerialPort>
#include "serialcontrol.h"
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int AvailablePorts=QSerialPortInfo::availablePorts().count();
QList<QSerialPortInfo> SerialObject=QSerialPortInfo::availablePorts();
cout << "There are total: " << SerialObject.count() << " available ports " << endl << endl;
QString description;
for (int i=0;i<AvailablePorts;i++)
{
cout << "The " << i+1 << " com port is :";
qDebug() << SerialObject[i].portName();
qDebug() << "Description : " << SerialObject[i].description();
qDebug() << "Manufacturer: " << SerialObject[i].manufacturer();
cout << endl;
}
SerialControlThread *RunThread=new SerialControlThread(SerialObject[0].portName(),&a);
cout << RunThread->openSerialPort() << endl;
RunThread->StarttoRun();
cout << "I am out of here" << endl;
delete RunThread;
return a.exec();
}
I wish to close the thread( back to the main function) when the buffer has received more than 4 data, but it doesn't.
It is my output
There are total: 1 available ports
The 1 com port is :"COM8"
Description : "MSP430 Application UART"
Manufacturer: "Texas Instruments"
1
0
1
2
3
I am bigger than 4
4
I am bigger than 4
5
I am bigger than 4
6
I am bigger than 4
7
I am bigger than 4
8
I am bigger than 4
9
I am bigger than 4
Apparently, the program gets stuck in a loop. I have tried some solutions, but none of these worked.
StartToRun calls QThread::exec in the wrong thread: you call it in the main thread, but it's supposed to be called in the thread itself - from within run().
Alas, SerialControlThread doesn't have to be a thread. Making it a thread forces it to be used in a dedicated thread - that should be a choice left to its user. Perhaps the thread would be shared among other serial controllers, or perhaps it'll do just fine in the main thread. Thus, it should be an object that handles serial data, that has a thread-safe interface so that you can move it to another thread if you wish - but would still work fine in the main thread, and thus has to handle data asynchronously without blocking.
Considering whether one needs to control the worker thread's run status so tightly: an idle thread consumes no resources - its event loop is blocked waiting on new events, its stack eventually gets paged out if there's memory pressure. If one intends to "wake" the thread for each operation, there's no need to be explicit about it: the event loop in the thread behaves that way be default and by design: it wakes when there are new events, such as incoming data, otherwise it sleeps. One shouldn't be stopping the thread then.
The example below shows a very minimal implementation. On the whole it's not very useful other than to demonstrate brevity as a contrast to the length of code in the question - in spite of identical limited functionality. Presumably you have a more complex communications protocol that you wish to handle. You may wish to consider the use of QDataStream read transactions to make the reader code more expressive, and using a state machine to represent the protocol.
// https://github.com/KubaO/stackoverflown/tree/master/questions/serial-galore-42241570
#include <QtWidgets>
#include <QtSerialPort>
// See https://stackoverflow.com/q/40382820/1329652
template <typename Fun> void safe(QObject * obj, Fun && fun) {
Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
if (Q_LIKELY(obj->thread() == QThread::currentThread() || !obj->thread()))
return fun();
struct Event : public QEvent {
using F = typename std::decay<Fun>::type;
F fun;
Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
Event(const F & fun) : QEvent(QEvent::None), fun(fun) {}
~Event() { fun(); }
};
QCoreApplication::postEvent(
obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun)));
}
class SerialController : public QObject {
Q_OBJECT
QSerialPort m_port{this};
QByteArray m_rxData;
void onError(QSerialPort::SerialPortError error) {
Q_UNUSED(error);
}
void onData(const QByteArray & data) {
m_rxData.append(data);
qDebug() << "Got" << m_rxData.toHex() << "(" << m_rxData.size() << ") - done.";
emit hasReply(m_rxData);
}
void onData() {
if (m_port.bytesAvailable() >= 4)
onData(m_port.readAll());
}
public:
explicit SerialController(const QString & port, QObject * parent = nullptr) :
QObject{parent}
{
m_port.setPortName(port);
connect(&m_port, static_cast<void(QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
this, &SerialController::onError);
}
~SerialController() { qDebug() << __FUNCTION__; }
bool open() {
m_port.setBaudRate(QSerialPort::Baud9600);
m_port.setDataBits(QSerialPort::Data8);
m_port.setParity(QSerialPort::NoParity);
m_port.setStopBits(QSerialPort::OneStop);
m_port.setFlowControl(QSerialPort::NoFlowControl);
return m_port.open(QIODevice::ReadWrite);
}
/// This method is thread-safe.
void start() {
safe(this, [=]{
m_port.write("S");
qDebug() << "Sent data";
});
}
Q_SIGNAL void hasReply(const QByteArray &);
void injectData(const QByteArray & data) {
onData(data);
}
};
QDebug operator<<(QDebug dbg, const QSerialPortInfo & info) {
dbg << info.portName();
if (!info.description().isEmpty())
dbg << " Description: " << info.description();
if (!info.manufacturer().isEmpty())
dbg << " Manufacturer: " << info.manufacturer();
return dbg;
}
// A thread that starts on construction, and is always safe to destruct.
class RunningThread : public QThread {
Q_OBJECT
using QThread::run; // final
public:
RunningThread(QObject * parent = nullptr) : QThread(parent) { start(); }
~RunningThread() { qDebug() << __FUNCTION__; quit(); wait(); }
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
auto const ports = QSerialPortInfo::availablePorts();
if (ports.isEmpty())
qFatal("No serial ports");
int n{};
qDebug() << "Available ports:";
for (auto & port : ports)
qDebug() << "port[" << n++ << "]: " << port;
SerialController ctl{ports.at(5).portName()};
if (!ctl.open())
qFatal("Open Failed");
// Optional: the controller will work fine in the main thread.
if (true) ctl.moveToThread(new RunningThread{&ctl}); // Owns its thread
// Let's pretend we got a reply;
QTimer::singleShot(1000, &ctl, [&ctl]{
ctl.injectData("ABCD");
});
QObject::connect(&ctl, &SerialController::hasReply, ctl.thread(), &QThread::quit);
QObject::connect(&ctl, &SerialController::hasReply, [&]{
qDebug() << "The controller is done, quitting.";
app.quit();
});
ctl.start();
return app.exec();
}
#include "main.moc"
I am trying to teach myself C++11 threading, and I would like to start a background producer thread at the beginning of the application, and have it run until application exit. I would also like to have consumer thread (which also runs for the life of the application).
A real-world example would be a producer thread listening on a Com port for incoming GPS data. Once a full message had been accumulated, it could be parsed to see if it was a message of interest, then converted into a string (say), and 'delivered back' to be consumed (update current location, for example).
My issue is I haven't been able to figure out how to do this without blocking the rest of the application when I 'join()' on the consumer thread.
Here is my very simplified example that hopefully shows my issues:
#include <QCoreApplication>
#include <QDebug>
#include <thread>
#include <atomic>
#include <iostream>
#include <queue>
#include <mutex>
#include <chrono>
#include "threadsafequeuetwo.h"
ThreadSafeQueueTwo<int> goods;
std::mutex mainMutex;
std::atomic<bool> isApplicationRunning = false;
void theProducer ()
{
std::atomic<int> itr = 0;
while(isApplicationRunning)
{
// Simulate this taking some time...
std::this_thread::sleep_for(std::chrono::milliseconds(60));
// Push the "produced" value onto the queue...
goods.push(++itr);
// Diagnostic printout only...
if ((itr % 10) == 0)
{
std::unique_lock<std::mutex> lock(mainMutex);
std::cout << "PUSH " << itr << " on thread ID: "
<< std::this_thread::get_id() << std::endl;
}
// Thread ending logic.
if (itr > 100) isApplicationRunning = false;
}
}
void theConsumer ()
{
while(isApplicationRunning || !goods.empty())
{
int val;
// Wait on new values, and 'pop' when available...
goods.waitAndPop(val);
// Here, we would 'do something' with the new values...
// Simulate this taking some time...
std::this_thread::sleep_for(std::chrono::milliseconds(10));
// Diagnostic printout only...
if ((val % 10) == 0)
{
std::unique_lock<std::mutex> lock(mainMutex);
std::cout << "POP " << val << " on thread ID: "
<< std::this_thread::get_id() << std::endl;
}
}
}
int main(int argc, char *argv[])
{
std::cout << "MAIN running on thread ID: "
<< std::this_thread::get_id() << std::endl;
// This varaiable gets set to true at startup, and,
// would only get set to false when the application
// wants to exit.
isApplicationRunning = true;
std::thread producerThread (theProducer);
std::thread consumerThread (theConsumer);
producerThread.detach();
consumerThread.join(); // BLOCKS!!! - how to get around this???
std::cout << "MAIN ending on thread ID: "
<< std::this_thread::get_id() << std::endl;
}
The ThreadSafeQueueTwo class is the thread safe queue implementation taken almost exactly as is from the "C++ Concurrency In Action" book. This seems to work just fine. Here it is if anybody is interested:
#ifndef THREADSAFEQUEUETWO_H
#define THREADSAFEQUEUETWO_H
#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>
template<typename T>
class ThreadSafeQueueTwo
{
public:
ThreadSafeQueueTwo()
{}
ThreadSafeQueueTwo(ThreadSafeQueueTwo const& rhs)
{
std::lock_guard<std::mutex> lock(myMutex);
myQueue = rhs.myQueue;
}
void push(T newValue)
{
std::lock_guard<std::mutex> lock(myMutex);
myQueue.push(newValue);
myCondVar.notify_one();
}
void waitAndPop(T& value)
{
std::unique_lock<std::mutex> lock(myMutex);
myCondVar.wait(lock, [this]{return !myQueue.empty(); });
value = myQueue.front();
myQueue.pop();
}
std::shared_ptr<T> waitAndPop()
{
std::unique_lock<std::mutex> lock(myMutex);
myCondVar.wait(lock, [this]{return !myQueue.empty(); });
std::shared_ptr<T> sharedPtrToT (std::make_shared<T>(myQueue.front()));
myQueue.pop();
return sharedPtrToT;
}
bool tryPop(T& value)
{
std::lock_guard<std::mutex> lock(myMutex);
if (myQueue.empty())
return false;
value = myQueue.front();
myQueue.pop();
return true;
}
std::shared_ptr<T> tryPop()
{
std::lock_guard<std::mutex> lock(myMutex);
if (myQueue.empty())
return std::shared_ptr<T>();
std::shared_ptr<T> sharedPtrToT (std::make_shared<T>(myQueue.front()));
myQueue.pop();
return sharedPtrToT;
}
bool empty()
{
std::lock_guard<std::mutex> lock(myMutex);
return myQueue.empty();
}
private:
mutable std::mutex myMutex;
std::queue<T> myQueue;
std::condition_variable myCondVar;
};
#endif // THREADSAFEQUEUETWO_H
Here's the output:
I know there are obvious issues with my example, but my main question is how would I run something like this in the background, without blocking the main thread?
Perhaps an even better way of trying to solve this is, is there a way that every time the producer has 'produced' some new data, could I simply call a method in the main thread, passing in the new data? This would be similar to queued signal/slots it Qt, which I am big fan of.
I'm trying to share a std::map<std::string, std::chrono::system_clock::time_point> map: each string is a hostname identifying a site, and the time_point is the last time a process visited that site.
I was trying with mmap but each process still see its own copy of the map.
Here's my code (I took away all the methods and variables not concerning my problem):
#include <sys/mman.h>
#include <unistd.h>
#include <iostream>
#include <map>
#include <string>
#include <chrono>
typedef std::map<std::string, std::chrono::system_clock::time_point> mymap;
typedef mymap::iterator iter;
typedef mymap* mapPointer;
class MmapManager {
private:
MmapManager() {
frequency = (mapPointer) mmap(NULL, sizeof(frequency), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (frequency == MAP_FAILED) {
std::cout << "mapping failed" << std::endl;
}
};
~MmapManager() {
std::cout << "~MmapManager()" << std::endl;
}
public:
// my class was designed with the singleton pattern
static MmapManager& getInstance() {
static MmapManager instance;
return instance;
}
private:
// pointer to my map
mapPointer frequency;
public:
// check if the process already visited site "host"
bool isHostAlreadyVisited(std::string host) {
return frequency->find(host) != frequency->end();
}
// add new visited site and time of the visit
void addHost(std::string host) {
(*frequency)[host] = std::chrono::system_clock::now();
std::cout << "PROC " << getpid() << " added " << host << std::endl;
}
// get time of the visit for site "host"
std::chrono::system_clock::time_point getElement(std::string host) {
return (*frequency)[host];
}
// print the map
void showMap(void) {
std::cout << "PROC " << getpid() << " prints map keys" << std::endl;
for (auto it = frequency->begin(); it != frequency->end(); ++it) {
std::cout << it->first << std::endl;
}
}
};
int main(void) {
// simulate the processes
for (int i=0; i<5; i++) {
// child process
if (fork() == 0) {
// if child never visited this site...
if (! MmapManager::getInstance().isHostAlreadyVisited("www.google.com")) {
std::cout << "PID " << getpid() << " www.google.com is new" << std::endl;
// ...add it to the map
MmapManager::getInstance().addHost("www.google.com");
}
else {
// if child already visited it, calculate
// how much time passed since last visit
auto now = std::chrono::system_clock::now();
auto before = MmapManager::getInstance().getElement("www.google.com");
std::chrono::duration<double> diff = now-before;
std::cout << "PID " << getpid() << " visited www.google.com " << diff.count() << " seconds ago" << std::endl;
}
MmapManager::getInstance().showMap();
_exit(EXIT_SUCCESS);
}
}
return 0;
}
Here's one of the possible outputs:
PID 12457 www.google.com is new
PID 12459 www.google.com is new
PID 12458 www.google.com is new
PID 12460 www.google.com is new
PID 12461 www.google.com is new
I can't use other external libraries like Boost or use threads: I know they share memory, but the program was designed this way (with child processes doing stuff) and I can't modify it (original code is not mine).
Why does each process still see its own copy of the map?
Edit: I think I did all the things you suggested me:
insertion in map is protected with a lock mechanism (thanks kfsone);
created a custom allocator for string and another for map (thanks Maxim Egorushkin for these two);
map is allocated before forking (thanks Zan Lynx).
The output is not different and map is still not shared:
MmapManager()
printMap
map empty
PID 5085 www.google.com is new
PID 5086 www.google.com is new
PROC 5086 added www.goole.com
PROC 5085 added www.goole.com
PID 5087 www.google.com is new
PROC 5087 added www.goole.com
You suggested me to use Boost but I'd like to use it after my code will work: I'm not reinventing the wheel, just learning the hard way.
Here follows my new code:
#include <sys/mman.h>
#include <unistd.h>
#include <sys/shm.h> /* shmat(), IPC_RMID */
#include <semaphore.h> /* sem_open(), sem_destroy(), sem_wait().. */
#include <fcntl.h> /* O_CREAT, O_EXEC */
#include <stdlib.h>
#include <iostream>
#include <map>
#include <string>
#include <chrono>
#include <cstddef>
#include <vector>
#include <limits>
#include <memory>
template<typename T> class stringAllocator {
public :
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T const * const_pointer;
typedef T& reference;
typedef T const & const_reference;
template<typename U> struct rebind {
typedef stringAllocator<U> other;
};
pointer address (reference value ) const {
return &value;
}
const_pointer address (const_reference value) const {
return &value;
}
size_type max_size () const throw() {
return std::numeric_limits <size_type>::max() / sizeof(T);
}
stringAllocator () throw () {}
stringAllocator (stringAllocator const &) throw () {}
template <typename U>
stringAllocator(stringAllocator <U> const &) throw () {}
~stringAllocator() throw () {}
pointer allocate (size_type n) {
pointer ptr = (pointer)malloc(n * sizeof(value_type));
return ptr;
}
void deallocate (pointer p, size_type n) {
free(p);
}
void construct (pointer p, const_reference value) {
new(p) T(value);
}
void destroy (pointer p) {
p->~T();
}
};
template <class T1, class T2>
bool operator==(const stringAllocator<T1>&, const stringAllocator<T2>&) throw() {
return true;
}
template <class T1, class T2>
bool operator!=(const stringAllocator<T1>&, const stringAllocator<T2>&) throw() {
return false;
}
typedef std::basic_string<
char,
std::char_traits<char>,
stringAllocator<char>
> myString;
/*************************************** map allocator ****************************************/
template<typename T> class mapAllocator{
public :
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template<typename U>
struct rebind {
typedef mapAllocator<U> other;
};
mapAllocator() throw() {}
mapAllocator (mapAllocator const &) throw () {}
~mapAllocator() throw () {}
template<typename U>
mapAllocator(mapAllocator<U> const&) {}
pointer address(reference r) { return &r; }
const_pointer address(const_reference r) { return &r; }
pointer allocate(size_type cnt, typename std::allocator<void>::const_pointer = 0) {
pointer new_memory = reinterpret_cast<pointer>(::operator new(cnt * sizeof (T)));
return new_memory;
}
void deallocate(pointer p, size_type n) {
::operator delete(p);
}
size_type max_size() const {
return std::numeric_limits<size_type>::max() / sizeof(T);
}
void construct(pointer p, const T& t) {
new(p) T(t);
}
void destroy(pointer p) {
p->~T();
}
};
template <class T1, class T2>
bool operator==(const mapAllocator<T1>&, const mapAllocator<T2>&) throw() {
return true;
}
template <class T1, class T2>
bool operator!=(const mapAllocator<T1>&, const mapAllocator<T2>&) throw() {
return false;
}
/*************************************** end map allocator ****************************************/
// class compare for map with std::string as Key
class strless {
public:
bool operator() (const myString first, const myString second ) const {
return first.compare(second) < 0;
}
};
template<typename Key, typename T>
using Map = std::map<
Key, // class Key
T, // class T
strless, // class Compare = std::less<Key>
mapAllocator<std::pair<const Key, T> // class Allocator = std::allocator<std::pair<const Key, T> >
>
>;
// typedef for the actual map I need to share between processes
typedef Map<myString, std::chrono::system_clock::time_point> frequencyMap;
class MmapManager {
private:
MmapManager() {
std::cout << "MmapManager()" << std::endl;
semMmap = sem_open("semaphore", O_CREAT|O_EXCL, 0644, 1);
sem_unlink("semaphore");
};
~MmapManager() {
std::cout << "~MmapManager()" << std::endl;
}
public:
static MmapManager& getInstance() {
static MmapManager instance;
return instance;
}
private:
frequencyMap fmap;
sem_t *semMmap;
public:
void start(void) {}
bool isHostAlreadyVisited(myString host) {
return fmap.find(host) != fmap.end();
}
void addHost(myString host) {
sem_wait(semMmap);
fmap[host] = std::chrono::system_clock::now();
sem_post(semMmap);
std::cout << "PROC " << getpid() << " added " << host << std::endl;
}
// get time of the visit for site "host"
std::chrono::system_clock::time_point getElement(myString host) {
return fmap[host];
}
void printMap(void) {
std::cout << "printMap" << std::endl;
if (!fmap.empty()) {
for (auto it : fmap) {
std::cout << it.first << ' ';
}
std::cout << std::endl;
} else {
std::cout << "map empty" << std::endl;
}
}
};
int main(void) {
MmapManager::getInstance().start();
for (int i=0; i<3; i++) {
if (fork() == 0) {
if (!MmapManager::getInstance().isHostAlreadyVisited("www.google.com")) {
std::cout << "PID " << getpid() << " www.google.com is new" << std::endl;
MmapManager::getInstance().addHost("www.goole.com");
}
else {
// if child already visited it, calculate
// how much time passed since last visit
auto now = std::chrono::system_clock::now();
auto before = MmapManager::getInstance().getElement("www.google.com");
std::chrono::duration<double> diff = now-before;
std::cout << "PID " << getpid() << " visited www.google.com " << diff.count() << " seconds ago" << std::endl;
}
_exit(EXIT_SUCCESS);
}
}
MmapManager::getInstance().printMap();
return 0;
}
This does not work because although you placed the container object into the shared memory, the elements are still allocated from the heap and thus they are not accessible by other processes.
You need a custom allocator that allocates elements in the shared memory. See Creating maps in shared memory for how it is done.
Note that the string class you use must also allocate memory from the shared memory.
In other words, you cannot have pointers to heap memory in the shared memory, because heap memory is not shared between processes. std classes have an allocator template argument, the default one allocates memory from the heap. This needs to be changed to a shared memory allocator to be able to share such objects via shared memory.
Another reason your code doesn't work is that you only create the maps after you called fork().
If you want your MAP_SHARED|MAP_ANONYMOUS map to be seen by all the children then you have to call mmap() before forking.
I have created a simple threaded TCP server which collects 3 lines read from the socket, and then tries to echo them back to the socket. The function echoCommand below crashes.
#include "fortunethread.h"
#include <QtNetwork>
#include <QDataStream>
FortuneThread::FortuneThread(int socketDescriptor, QObject *parent)
: QThread(parent), socketDescriptor(socketDescriptor), in(0)
{
}
void FortuneThread::run()
{
tcpSocketPtr = new QTcpSocket;
if (!tcpSocketPtr->setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocketPtr->error());
return;
}
in = new QDataStream(tcpSocketPtr);
connect(tcpSocketPtr, SIGNAL(readyRead()), this, SLOT(readCommand()) );
QThread::exec();
}
void FortuneThread::echoCommand()
{
QString block;
QTextStream out(&block, QIODevice::WriteOnly);
for (QStringList::Iterator it = commandList.begin(); it != commandList.end(); ++it) {
out << "Command: " << *it << endl;
}
out << endl;
tcpSocketPtr->write(block.toUtf8());
tcpSocketPtr->disconnectFromHost();
tcpSocketPtr->waitForDisconnected();
}
void FortuneThread::readCommand()
{
while (tcpSocketPtr->canReadLine())
{
commandList << (tcpSocketPtr->readLine()).trimmed();
}
if (commandList.size() > 2)
{
echoCommand();
}
}
and here is the file where I connect up the slots/signals:
#include "fortuneserver.h"
#include "fortunethread.h"
#include <stdlib.h>
FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{
}
void FortuneServer::incomingConnection(qintptr socketDescriptor)
{
QString fortune = fortunes.at(qrand() % fortunes.size());
FortuneThread *thread = new FortuneThread(socketDescriptor, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
during or after the socket write, with this error:
**QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x7f19cc002720), parent's thread is FortuneThread(0x25411d0), current thread is QThread(0x220ff90)**
Since I create the tcpSocketPtr in the run() function, I know it is in the same thread as this function. Why would the socket write fail? I should point out that the write is succeeding since I see the output on the telnet window...but still the socket write fails...
Just more info...I found that I should NOT put a slot in a QThread..not sure how to get around this, but here is my class definiation:
class FortuneThread : public QThread
{
Q_OBJECT
public:
FortuneThread(int socketDescriptor, QObject *parent);
void run();
signals:
void error(QTcpSocket::SocketError socketError);
private slots:
void readCommand();
private:
void echoCommand();
int socketDescriptor;
QDataStream *in;
QStringList commandList;
QTcpSocket *tcpSocketPtr;
};
I am using Qt to generate a Window. Additionally I use libnfc to get access to a nfc reader, so far so good.
In my self written nfc-class i generate a new thread, this thread is polling for new tags on the reader. If there is a new tag, the thread will start a signal event for the MainWindow.
In the main window I have just a QWebView which will show different websites on different states (after start, new tag, tag removed), just realy basic stuff.
My problem is now: that the main window (or the QWebView) is not updating. If i switch to another programm and go back to my app, the window will be updated. I was already searching with google and trying different stuff but nothing helps.
Here the thread code:
class NFC_Thread : public QThread
{
Q_OBJECT
public:
NFC_Thread(NFC_Reader * Reader);
void run();
signals:
void NewTarget(nfc_target Target);
void TargetRemoved(nfc_target Target);
private:
int mError;
bool mStopPolling;
};
void NFC_Thread::run()
{
mError = 0;
mStopPolling = false;
while(!mStopPolling)
{
nfc_target Target;
mError = nfc_initiator_poll_target(mReader->GetDevice(), nmModulations, szModulations, mPollNr, mPollPeriod, &Target);
if(mError > 0)
{
cout << "NFC: found target" << endl;
}
#warning Bug in driver: Timeout generate a NFC_EIO Error, 'https://code.google.com/p/libnfc/issues/detail?id=224'
else if(mError > 0)
{
cout << "NFC: Error" << endl;
mStopPolling = true;
}
else
{
cout << "NFC: no target found" << endl;
}
}
}
MainWindow Code:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void SetNewTarget(nfc_target Target);
void doTargetRemoved(nfc_target Target);
private:
bool event(QEvent *event);
void resizeEvent(QResizeEvent *);
void adjust();
Ui::MainWindow *ui;
QWebView * mWebView;
};
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mWebView = new QWebView(this);
mWebView->load(QUrl("http://www.pbuchegger.at/"));
mWebView->show();
}
void MainWindow::SetNewTarget(nfc_target Target)
{
QString str = "NEW TARGET: \n";
{
char * s;
str_nfc_target(&s, Target, false);
str += s;
delete s;
}
//cout << "NFC: Target: " << str << endl;
mWebView->load(QUrl("http://www.google.at"));
update();
repaint();
mWebView->update();
qApp->processEvents();
/*QMessageBox msgBox;
msgBox.setText(str);
msgBox.exec();*/
}
void MainWindow::doTargetRemoved(nfc_target Target)
{
QString str = "TARGET REMOVED: \n";
{
char * s;
str_nfc_target(&s, Target, false);
str += s;
delete s;
}
//cout << "NFC: Target: " << str << endl;
mWebView->load(QUrl("http://www.cde.at"));
update();
repaint();
mWebView->update();
qApp->processEvents();
/*QMessageBox msgBox;
msgBox.setText(str);
msgBox.exec();*/
}
bool MainWindow::event(QEvent *event)
{
if(event->type() == QEvent::Resize)
{
adjust();
return true;
}
return false;
}
void MainWindow::resizeEvent(QResizeEvent *)
{
adjust();
}
void MainWindow::adjust()
{
mWebView->setGeometry(0, 0, ui->centralWidget->geometry().width(), ui->centralWidget->geometry().height());
}
main code:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qRegisterMetaType<nfc_target>("nfc_target");
MainWindow w;
w.setWindowState(Qt::WindowMaximized);
NFC_Reader Reader;
nfc_device_string devs;
size_t nr;
QString str = "";
Reader.GetDevices(devs, nr);
if(nr > 0)
{
if(!Reader.InitReader(NULL))
{
str += "Error on init!";
}
else
{
Reader.Start_Polling();
str += "Started Polling!";
}
}
else
{
str += "No Device found!";
}
w.SetText(str);
SignalHelper Helper;
QObject::connect(Reader.GetThread(), SIGNAL(NewTarget(nfc_target)), &Helper, SLOT(doNewTarget(nfc_target)));
QObject::connect(Reader.GetThread(), SIGNAL(TargetRemoved(nfc_target)), &Helper, SLOT(doTargetRemoved(nfc_target)));
QObject::connect(&Helper, SIGNAL(NewTarget(nfc_target)), &w, SLOT(SetNewTarget(nfc_target)));
QObject::connect(&Helper, SIGNAL(TargetRemoved(nfc_target)), &w, SLOT(doTargetRemoved(nfc_target)));
w.show();
int ret = a.exec();
Reader.Abort_Polling();
return ret;
}
As u can see, I have a "Helper" class, this class is just getting the signal in a slot and starting again a signal which will be forward to the mainwindow. If i want to forward the signal directly to the mainwindow, nothing is happening (like the signal is not fired), but i was checking it with the Qt-About box, and the box is showing up.
Helper class:
class SignalHelper : public QObject
{
Q_OBJECT
public slots:
void doNewTarget(nfc_target Target);
void doTargetRemoved(nfc_target Target);
signals:
void NewTarget(nfc_target Target);
void TargetRemoved(nfc_target Target);
};
void SignalHelper::doNewTarget(nfc_target Target)
{
emit NewTarget(Target);
}
void SignalHelper::doTargetRemoved(nfc_target Target)
{
emit TargetRemoved(Target);
}
no compiler errors or linker errors. this code shows just the important stuff, all the unimportant stuff is removed. just for your information the project file:
QT += core gui testlib
QT += webkit
greaterThan(QT_MAJOR_VERSION, 4) {
QT += widgets
}
TARGET = NFC_GUI
TEMPLATE = app
SOURCES += main.cpp \
mainwindow.cpp \
nfc_thread.cpp \
nfc_reader.cpp \
signal_helper.cpp
HEADERS += mainwindow.h nfc_thread.h nfc_reader.h signal_helper.h
FORMS += mainwindow.ui
LIBS += -lnfc
Making my comment an answer:
Your function
bool MainWindow::event(QEvent *event)
{
if(event->type() == QEvent::Resize)
{
adjust();
return true;
}
return false;
}
eats any event which is handled in QMainWindow except for QEvent::Resize. You need to call the default behaviour for events you are not interested in:
bool MainWindow::event(QEvent *event)
{
if(event->type() == QEvent::Resize)
{
adjust();
return true;
}
// call the parent implementation
return QMainWindow::event(event);
}
Note you can also just simply implement QWidget::resizeEvent:
void MainWindow::resizeEvent(QResizeEvent *event)
{
adjust();
QMainWindow::resizeEvent(event);
}
If you're calling slots from signals between different threads, you need to make the connect calls with Qt::QueuedConnection as the Connection Type.