NOTE: C++98
Hi, I'm a little new to c++ and I am writing a databaes program and am attempting to start a timer using the boost::asio package using pthread. The aim of the timer is to start after sql queries have been placed inside a buffer, of which will run an execute function if nothing has been received for a period of time. I have managed to get it to compile, but it doesn't look like the pthread instance is starting.
I have called the pthread inside my getInstance method, and the boost::asio alarm has been set up accordingly. What I will show below is that by calling io_run() directly starts the timer falls into a loop within the alarm.
database.h
void *run_io(void *arg);
class Database
{
private:
static Database *dbInstance; //= NULL;
public:
boost::asio::io_service io_service;
boost::posix_time::millisec interval;
boost::asio::deadline_timer timer;
pthread_t timerThread;
public:
static Database &getInstance()
{
if (!dbInstance)
{
dbInstance = new Database();
// pthread_create(&dbInstance->timerThread,NULL,run_io,&dbInstance->io_service);
std::cout << " INSTANCE CREATED " << std::endl;
pthread_create(&dbInstance->timerThread, NULL, run_io, (void *)&dbInstance->io_service);
// pthread_join(&dbInstance->timerThread, NULL);
}
return *dbInstance;
}
};
database.cpp
Database *Database::dbInstance = NULL;
Database::Database()
: interval(2000), timer(io_service, interval) {}
Database::~Database()
{
sqlite3_close(db);
}
void Database::setAlarm(const boost::system::error_code& /* e */)
{
std::cout << "[TEST] WE ARE IN SET ALARM " << std::endl;
DB_WRITE_TIME = 500;
boost::posix_time::milliseconds interval(DB_WRITE_TIME);
// Reschedule the timer for 1 second in the future:
timer.expires_at(timer.expires_at() + interval);
// Posts the timer event
timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
}
int Database::buffer()
{
// DO BUFFER STUFF
timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
// io_service.run() <-- uncommenting this results in the loop
return rc ;
}
void *run_io(void *arg)
{
boost::asio::io_service *io_service = (boost::asio::io_service *)arg;
io_service->run();
}
So I don't feel like the pthread is even starting. I tried putting a print statement in there to see if it came out, and nothing appeared in my terminal.
---- EDIT ----
I have made changes as per Sehe's advice, however it still does not look like I am able to call the alarm handler (setAlarm()). I had to slightly modify it to be compatible with the whole program, but essentially it is this (I gave the interval time a value of 5000 to give it enough time for the tests):
database.h
class Database
{
private:
static boost::shared_ptr<Database> dbInstance;
private:
typedef boost::asio::io_service io_service;
io_service io;
boost::scoped_ptr<io_service::work> work;
boost::posix_time::millisec interval;
boost::asio::deadline_timer timer;
boost::thread timerThread;
void run_io()
{
std::cout << "ENTER IO THREAD" << std::endl;
io.run();
std::cout << "LEAVE IO THREAD" << std::endl;
}
public:
static Database &getInstance()
{
if (!dbInstance)
{
std::cout << " INSTANCE CREATED " << std::endl;
dbInstance.reset(new Database());
dbInstance->timerThread = boost::thread(boost::bind(&Database::run_io,dbInstance));
}
return *dbInstance;
}
Database(); // <-- default constructor (doesn't take any args)
~Database();
database.cpp
boost::shared_ptr<Database> Database::dbInstance;
static const int DB_WRITE_TIME = 5000;
Database::Database()
: work(new io_service::work(io)), interval(5000), timer(io, interval)
{
// std::cout << " CONSTRUCTED " << std::endl;
}
Database::~Database()
{
// std::cout << " DESTROYED " << std::endl;
// sqlite3_close(db);
}
void Database::setAlarm(const boost::system::error_code& ec)
{
std::cout << "[TEST] WE ARE IN SET ALARM - ec message = " << ec.message() << std::endl;
executeSqlInBuffer(); // once timer expire, call the execute function
if(!ec)
{
boost::posix_time::milliseconds interval(DB_WRITE_TIME);
timer.expires_from_now(interval);
timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
}
}
void Database::teardown()
{
// std::cout << " INSTANCE SHUTTING DOWN " << std::endl;
timer.cancel(); // stop timer loop
work.reset(); // allows io.run() to exit
if(timerThread.joinable())
{
std::cout << " JOINED " << std::endl;
timerThread.join(); // releasing bound of shared_ptr
}
else std::cout << " NOT JOINED " << std::endl;
dbInstance.reset(); // releasing instance
}
int Database::buffer()
{
// do buffering
if(buffer.size() == max_size)
{
executeSqlInBuffer();
}
std::cout << timer.expires_from_now(interval) << std::endl;
// std::cout << " ~ BEFORE TIMER ~ " << std::endl;
timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
return 1;
}
main.cpp
int main()
{
pthread_t thread1; // a few pthreads in main that handle other areas of the program.
pthread_create(&thread1,NULL,thread1Arg,NULL);
pthread_t dbThread; // my pthread for the database
pthread_create(&dbThread,NULL,dbThreadArg,NULL);
Database& database = Database::getInstance();
database.teardown();
pthread_join(thread1,NULL);
pthread_join(dbThread,NULL);
return 0;
}
You can also see here that it enters and leaves the IO thread, and creates an instance, plus the debug output for timer.expires_from_now(interval):
INSTANCE CREATED
JOINED
ENTER IO THREAD
LEAVE IO THREAD
...
...
0 ---> first cycle
1 ---> second cycle
...
1 ---> nth cycle
I'm very ccnfused why anyone who uses Boost or C++11 (or both...) would ever use raw pthread threads (see e.g. C++ boost asynchronous timer to run in parallel with program for a good juxtaposition).
The real problem is likely that you have io_service running out of work (see e.g. https://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/reference/io_service__work.html).
If you have no pending async operations the thread just exits.
Another problem is accuracy issues with
timer.expires_at(timer.expires_at() + interval);
It's possible that some handlers take so much time that by the time you schedule your next alarm, the deadline has already expired. It's probably better to use
timer.expires_from_now(interval);
Note this also matches the comment better. The comment suffers from comment already because it says "1 second" but it is actually some defined constant DB_WRITE_TIME
or separate your timer from the other handlers in some other way to guarantee accurate scheduling.
Finally, you had UB due to the absense of any shutdown. The static instance never gets destroyed, but what's worth the non-detached thread never is joined, creating undefined behaviour at shutdown.
This problem is actually almost identical to the one recently discussed here, where I also explains the way work guards work in more detail: asio::io_service is ending immediately with work
Here's a c++11 rewrite with the necessary fix:
Since I now noticed you're that person stuck in c++03 land for some weird reason, a Boost Thread version:
C++03 DEMO/Boost Thread
Live On Coliru
#include <boost/asio.hpp>
#include <boost/make_shared.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/thread.hpp>
#include <iostream>
static const int DB_WRITE_TIME = 500;
class Database
{
private:
static boost::shared_ptr<Database> dbInstance;
Database()
: work(new io_service::work(io)),
interval(750),
timer(io, interval)
{
std::cout << "INSTANCE CREATED" << std::endl;
}
void on_timer_completed(const boost::system::error_code& ec) {
std::cout << "[on_timer_completed] " << ec.message() << std::endl;
if (!ec) {
boost::posix_time::milliseconds interval(DB_WRITE_TIME);
// Reschedule the timer
timer.expires_from_now(interval);
timer.async_wait(boost::bind(&Database::on_timer_completed, this, _1));
}
}
int buffer()
{
// DO BUFFER STUFF
timer.expires_from_now(interval);
timer.async_wait(boost::bind(&Database::on_timer_completed, this, _1));
// io_service.run() <-- uncommenting this results in the loop
return 1; // rc ;
}
public:
void do_stuff() {
buffer(); // whatever it does
}
void teardown() {
std::cout << "INSTANCE SHUTTING DOWN\n";
timer.cancel(); // stop timer loop
work.reset(); // allows io.run() to exit
if (timerThread.joinable()) {
timerThread.join(); // releasing the bound shared_ptr
}
dbInstance.reset(); // releasing the instance
}
~Database() {
//sqlite3_close(db);
std::cout << "INSTANCE DESTROYED\n";
}
private:
typedef boost::asio::io_service io_service;
io_service io;
boost::scoped_ptr<io_service::work> work;
boost::posix_time::millisec interval;
boost::asio::deadline_timer timer;
boost::thread timerThread;
void run_io() {
std::cout << "ENTER IO THREAD" << std::endl;
io.run();
std::cout << "LEAVE IO THREAD" << std::endl;
}
public:
static Database &getInstance()
{
if (!dbInstance)
{
dbInstance.reset(new Database());
dbInstance->timerThread =
boost::thread(boost::bind(&Database::run_io, dbInstance));
}
return *dbInstance;
}
};
boost::shared_ptr<Database> Database::dbInstance;
int main() {
Database& db = Database::getInstance();
boost::this_thread::sleep_for(boost::chrono::seconds(1));
db.do_stuff();
boost::this_thread::sleep_for(boost::chrono::seconds(3));
// ....
db.teardown();
}
Prints
INSTANCE CREATED
ENTER IO THREAD
[on_timer_completed] Success
[on_timer_completed] Success
[on_timer_completed] Success
[on_timer_completed] Success
[on_timer_completed] Success
INSTANCE SHUTTING DOWN
[on_timer_completed] Operation canceled
LEAVE IO THREAD
INSTANCE DESTROYED
Related
I'm trying to send some data from QLocalSocket to QLocalSever in a loop. Sever only gets the first data and not receiving subsequent data, but if I introduce 1 mec delay between each call from the client then the server starts to receive everything. Please check out the below Client & Server code.
client.cpp
#include "client.h"
#include "QDataStream"
#include <QTest>
TestClient::TestClient() : m_socket{new QLocalSocket(this)}{
m_socket->connectToServer("TestServer");
if (m_socket->waitForConnected(1000)) {
qDebug("socket Connected!");
}
connect(m_socket, &QLocalSocket::readyRead, this, &TestClient::onNewData);
}
void TestClient::onNewData() {
qCritical() << "data received from server";
}
void TestClient::sendDataToServer() {
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_10);
QString testString = "test data";
out << quint32(testString.size());
out << testString;
m_socket->write(block);
m_socket->flush();
}
void TestClient::startClient() {
for(int i = 0; i < 5; i++) {
//QTest::qWait(1); //works if I uncomment this line
sendDataToServer();
}
}
server.cpp
#include "server.h"
TestServer::TestServer() : m_server{new QLocalServer(this)} {
QLocalServer::removeServer("TestServer");
if (!m_server->listen("TestServer")) {
qCritical() << "couldn't connect to server";
}
connect(m_server, &QLocalServer::newConnection, this, &TestServer::onNewConnection);
}
void TestServer::onNewConnection() {
m_socket = m_server->nextPendingConnection();
connect(m_socket, &QLocalSocket::readyRead, this,
&TestServer::onNewData);
connect(m_socket, &QLocalSocket::disconnected, m_socket,
&QLocalSocket::deleteLater);
}
void TestServer::onNewData() {
QLocalSocket* client = qobject_cast<QLocalSocket*>(sender());
client->readAll();
qCritical() << "data read by server";
}
from the qt doc, it's stated that
readyRead() is not emitted recursively; if you reenter the event loop
or call waitForReadyRead() inside a slot connected to the readyRead()
signal, the signal will not be reemitted (although waitForReadyRead()
may still return true).
so is this my problem? adding timer is the only solution here?
You can compile this test project -> http://www.filedropper.com/testsocket
I want to make simple communication example on Qt Remote Objects. I want to make the communication peer-to-peer, therefore I'm trying to merge both Source and Replica of the same remote object functionality in one application (REPC_MERGED tool used to generate Source and Replica base classes).
#include <QtCore/QCoreApplication>
#include "MyPeerHost.h"
#include "Client.h"
#include <QDebug>
static QString peer_node_name(int number)
{
QString ret = QString("peer_%1").arg(number);
return ret;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyPeerHost peerHost; // just inherits auto-generated MyPeerSimpleSource
QUrl thisAddress = "local:" + peer_node_name(0);
QRemoteObjectHost sourceNode(thisAddress);
if(sourceNode.enableRemoting(&peerHost))
{
qInfo() << "Source remoting enabled successfully" << thisAddress;
QUrl remoteAddress = "local:" + peer_node_name(1);
QSharedPointer<MyPeerReplica> replica;
QRemoteObjectNode replicaNode;
if(replicaNode.connectToNode(remoteAddress))
{
qInfo() << "Replica connected to the address" << remoteAddress << "successfully";
replica.reset(replicaNode.acquire<MyPeerReplica>());
QString sourceClassName = peerHost.staticMetaObject.className();
qDebug() << "Replica wait for Source" << sourceClassName << "...";
if(replica->waitForSource(1000))
{
qInfo() << "Replica object completely initialized";
Client client;
client.setReplicaObject(replica);
client.sendMessage("AAA");
}
else
{
qCritical() << "Replica wait for Source" << sourceClassName << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Replica connect to the address" << remoteAddress << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Source remoting enable FAILED" << sourceNode.lastError();
}
return a.exec();
}
Application output:
Source remoting enabled successfully QUrl("local:peer_0")
Replica connected to the address QUrl("local:peer_1") successfully
Replica wait for Source "MyPeerHost" ...
Replica wait for Source "MyPeerHost" FAILED QRemoteObjectNode::NoError
As you see, replicaNode successfully connected to the non-existent node QUrl("local:peer_1").
What I am doing wrong?
You don't have valid Qt code.
Qt relies on an event loop to handle asynchronous behavior, which is started by the a.exec() at the end of your main() routine. Qt Remote Objects, in turn, relies on the event loop for all of its communication.
In your code, you create your objects on the stack, but in code blocks that go out of scope before you start the event loop. They will therefore be destructed before the event loop is kicked off.
I'd recommend starting with some of the examples, make sure they work, then grow what you are trying to do from there.
There are various methods of reading and writing from a QTcpSocket using a QDatastream as seen here
The difference is, I will be sending more than "one packet" or blocks.
A basic implementation on the server (sending) side and client (recieving) is seen below - only the actual sending and receiving snippets are shown
More Info, What I tried:
When writing to a QTcpSocket, I attempted to use the QTcpSocket::canReadLine() however this fails straightup after the QTcpSocket::waitForReadReady() signal fires.
I then tried QDataStream::atEnd() in a while loop which causes a system crash :(
The code below shows my latest attempt of going through the QDataStream docs, and utilzing the commitTransaction where it states
If no full packet is received, this code restores the stream to the initial position, after which you need to wait for more data to arrive.
Under the heading Using Read Transactions. But ofcourse, this just reads one block that is sent, i.e the first block.
Question:
When writing to a QTcpSocket multiple times, and flushing the socket each time to send that data, how can I read this from a QTcpSocket as it is send, keep the original "send structure"?
The example below only reads the first block and ends. I would like to read the block containing "Response 2" and "Response 3".
Code Implementations:
//server.h
//...
QTcpSocket *clientSocket = nullptr;
QDataStream in;
//...
//server.cpp
//...
in.setDevice(clientSocket);
in.setVersion(QDataStream::Qt_4_0);
in.startTransaction();
QString nextFortune;
in >> nextFortune;
if (in.commitTransaction())
ui->lblOut->setText(nextFortune);
if (clientSocket != nullptr) {
if (!clientSocket->isValid()) {
qDebug() << "tcp socket invalid";
return;
}
if (!clientSocket->isOpen()) {
qDebug() << "tcp socket not open";
return;
}
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << QString(QString("Response:") + nextFortune);
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
// block.clear();
out << QString("Response number 2");
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
// block.clear();
out << QString("Response number 3 here, and this is the end!");
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
clientSocket->disconnectFromHost();
}
//...
And the client side
//client.h
//...
QTcpSocket *tcp_con = nullptr;
QDataStream in;
//...
//client.cpp
//...
if(!tcp_con->waitForReadyRead()){
qDebug(log_lib_netman_err) << "tcp con timeout for reading";
tcp_con->disconnectFromHost();
return ReturnObject(ReturnCode::SocketError, QString());
}
in.setDevice(tcp_con);
in.setVersion(QDataStream::Qt_4_0);
in.startTransaction();
QList<QString> data_rcv = QList<QString>();
QString s;
// while (tcp_con->canReadLine()) {
// in >> s;
// data_rcv.push_back(s);
// }
// while (!in.read) {
in >> s;
data_rcv.push_back(s);
// }
while (!in.commitTransaction()){
qDebug(log_lib_netman_info) << "waiting for more data";
in >> s;
data_rcv.push_back(s);
// qDebug(log_lib_netman_err) << "Unable to send data to server";
// tcp_con->disconnectFromHost();
// return ReturnObject(ReturnCode::FailedReceiving, QString());
}
// if (s.isEmpty()) {
// qDebug(log_lib_netman_err) << "Empty response recieved";
// tcp_con->disconnectFromHost();
// return ReturnObject(ReturnCode::NoDataRecieved, QString());
// }
tcp_con->disconnectFromHost();
return ReturnObject(ReturnCode::ReceivedSuccess, data_rcv);
Help would be greatly appreciated!
I have a sever client application and in the beginning client loads data from server. I have a dialog showing status of getting data from server (has progress bar). But when I call the function the dialog appears with no contents in it with white background and suddenly changes to completed status.
void SystemScreen::loadServerData()
{
qDebug() << Q_FUNC_INFO << "Invoked";
if (NULL != mpDataManagerDlg)
{
qDebug() << Q_FUNC_INFO << "show progres screen";
mpDataManagerDlg->showScreen();
}
loadData();
qDebug() << Q_FUNC_INFO << "Exits";
}
void SystemScreen::loadData()
{
qDebug() << Q_FUNC_INFO << "Invoked";
if (NULL != mpDataManager)
{
mpDataManager->loadDataFromServer();
}
qDebug() << Q_FUNC_INFO << "Exits";
}
I feel that dialog is displayed only after loadData() function is completed. Is there any alternative to do this?
I used a timer to start
QTimer::singleShot(100, this, SLOT(loadData()));
But then I have some trouble in getting data. ie data is empty if I read suddenly.
EDIT:
void DataManagerDialog::setDefault()
{
qDebug() << Q_FUNC_INFO << "Invoked";
setProgressBar(0);
setProgressBarColor(false);
ui->deptFailButton->hide();
ui->deptOkButton->hide();
ui->deptLabel->setStyleSheet("color:gray");
ui->subGroupFailButton->hide();
ui->subGroupOkButton->hide();
ui->subGroupLabel->setStyleSheet("color:gray");
ui->itemFailButton->hide();
ui->itemOkButton->hide();
ui->itemLabel->setStyleSheet("color:gray");
ui->salesBtnFailButton->hide();
ui->salesBtnOkButton->hide();
ui->salesBtnLabel->setStyleSheet("color:gray");
qDebug() << Q_FUNC_INFO << "Exits";
}
void DataManagerDialog::alignCenter()
{
qDebug() << Q_FUNC_INFO << "Invoked";
QWidget *par = parentWidget();
if (par)
{
int x = width()/2;
int y = height()/2;
QPoint mid(mapToGlobal(QPoint(x, y)));
int px = par->width()/2;
int py = par->height()/2;
QPoint parMid(mapToGlobal(QPoint(px, py)));
move(parMid.x()-mid.x(), parMid.y()-mid.y());
}
qDebug() << Q_FUNC_INFO << "Exits";
}
void DataManagerDialog::showScreen()
{
setDefault();
alignCenter();
show();
}
You probably do not enter the event loop.
Try to call QCoreApplication::processEvents() from time to time in mpDataManager->loadDataFromServer() to update the GUI.
From the processEvents documentation :
You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file).
Edit added after getting feedback from the comments
A better approach would be to send signals in your loadDataFromServer() method with the status information and have a slot listen to the signal and update the GUI.
Here a prototype illustrating the idea :
void mpDataManagerDlg::loadDataFromServer() {
while(true) {
// do some work
int progress = // some value
emit updateDialogSignal(progress);
}
}
// in your dialog class
public slots:
void DataManagerDialog::updateDialog(int progress) {
// update gui
}
More about signals and slots can be found here.
Can anyone help me read the output of qprocess after write and loop until all task is done?
I have this code
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
the expected output must be
"OK"
"scan results"
but the ouput is
"OK"
""
thanks.
Your multiple waits are not useful for anything. All you care about is when the process finishes, so have a single waitForFinished call with a much longer timeout (those scans don't happen in ~100ms, a few seconds is a good minimum).
You should not be using the blocking waitForXxx methods. They trip up everyone and are a source of unending grief. Forget that they exist. Use process's signals to react to events as they happen.
Qt 5 + C++11
This is the way forward. This is why you should insist on using a modern development environment, if you can. It's less typing and easier to understand.
void MyObject::startWifi() {
auto process = new QProcess(this);
process->start("program", QStringList() << "argument");
connect(process, &QProcess::started, [process]{
process->write("scan\n");
process->closeWriteChannel();
});
connect(process, &QProcess::finished, [process]{
qDebug() << process->readAllStandardOutput();
process->deleteLater();
});
}
Qt 4
class MyObject : public QObject {
Q_OBJECT
QProcess m_wifi;
Q_SLOT void onStarted() {
m_wifi.write("scan\n");
m_wifi.closeWriteChannel();
}
Q_SLOT void onFinished() {
qDebug() << m_wifi.readAllStandardOutput();
}
public:
MyObject(QObject * parent = 0) : QObject(parent) {
connect(&m_wifi, SIGNAL(started()), SLOT(onStarted()));
connect(&m_wifi, SIGNAL(finished(int,QProcess::ExitStatus)),
SLOT(onFinished()));
}
Q_SLOT void start() {
m_wifi.start("program", QStringList() << "argument");
}
};
Then invoke the start method/slot on an instance of this object. That's all.