I have a little simple program to test wether I can visualize a point cloud from a different thread and continue working in the main thread until typing 'q' in the terminal.
In Ubuntu 10.04, the code works, letting me visualize the cloud as new points are added to it in each iteration. However, in Windows 7 this dosn't work (I'm compiling it with QtCreator). The cloud is shown and new points are computed in each turn, but this never exits. When typing 'q', the loop stops but the visualization thread keeps running. The only way to stop execution is to explicitly use CTRL+C.
More things:
- If I don't uncomment the addPointCloud line before the !viewer->wasStopped() loop in the Visualize function, the point cloud is never shown. It doesn't matter that later in the loop I explicitly add it. It has to be done before the loop (now that line is commented to demonstrate that behaviour).
- I also tried to use boost::mutex instead of *tbb::queuing_mutex*, but again, the program won't exit.
Do you have any idea why the thread is never joining?. Also, constructive critics about my thread usage are always welcomed, I want to keep improving.
Here's the code:
#include <boost/thread/thread.hpp>
#include <iostream>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include "tbb/queuing_mutex.h"
typedef pcl::PointXYZ PointType;
typedef pcl::PointCloud<PointType> PointCloudType;
typedef tbb::queuing_mutex MutexType;
//typedef boost::mutex MutexType;
MutexType safe_update;
const unsigned int HEIGHT = 100;
const unsigned int WIDTH = 100;
bool has_to_update(true);
void Visualize(PointCloudType::Ptr cloud) {
pcl::visualization::PCLVisualizer* viewer = new pcl::visualization::PCLVisualizer("Vis in thread",true);
viewer->setBackgroundColor(1.0,0.0,0.0);
// viewer->addPointCloud<PointType>(cloud, "sample cloud");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
viewer->resetCamera();
while(!viewer->wasStopped()) {
viewer->spinOnce(100);
{
// boost::lock_guard<MutexType> lock(safe_update);
MutexType::scoped_lock lock(safe_update);
if(has_to_update) {
if(!viewer->updatePointCloud<PointType>(cloud, "sample cloud")) {
viewer->addPointCloud<PointType>(cloud, "sample cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud");
viewer->resetCamera();
}
has_to_update = false;
}
} // end scoped_lock
}
delete viewer;
};
int main(int argc, char** argv) {
PointCloudType::Ptr c(new PointCloudType);
c->height=HEIGHT;
c->width=WIDTH;
const unsigned int size( c->height*c->width);
c->points.resize(size);
for(unsigned int i(0);i<size;++i){
c->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
c->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
c->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
}
std::cout << "Filled cloud height: " << c->height << " ** widht = "
<< c->width << " ** size: " << c->points.size()
<< "\n"
;
boost::thread vis_thread( boost::bind( &Visualize, boost::ref(c) ) );
char exit;
std::vector<PointType> new_points;
new_points.resize(10);
PointType new_point;
while(exit!='q') {
for(unsigned int i(0);i<10;++i) {
new_point.x = 2000 * rand () / (RAND_MAX + 1.0f);
new_point.y = 2000 * rand () / (RAND_MAX + 1.0f);
new_point.z = 2000 * rand () / (RAND_MAX + 1.0f);
std::cout << "New point " << i << " with x = " << new_point.x
<< " ; y = " << new_point.y << " ; z = "
<< new_point.z << "\n"
;
new_points.push_back(new_point);
}
{
// boost::lock_guard<MutexType> lock(safe_update);
MutexType::scoped_lock lock(safe_update);
c->insert( c->points.end(), new_points.begin(), new_points.end() );
has_to_update = true;
} // end scoped_lock
std::cout << "Exit?: ";
std::cin>>exit;
}
vis_thread.join();
return 0;
}
Thanks for your time!.
EDIT: Since I can't use a debugger due to Windows not recognizing the executable format(?) I've put some qDebug()
lines over the Visualize
function (also, instead of directly calling viewer->wasStopped()
now I'm using a volatile intermediate var, stopped):
void Visualize(PointCloudType::Ptr cloud) {
pcl::visualization::PCLVisualizer* viewer = new pcl::visualization::PCLVisualizer("Vis in thread",true);
viewer->setBackgroundColor(1.0,0.0,0.0);
viewer->addPointCloud<PointType>(cloud, "sample cloud");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
viewer->resetCamera();
volatile bool stopped( false );
int iterations( -1 );
while(!stopped) {
++iterations;
qDebug() << "Before spinOnce - it: << iteration << "\n";
viewer->spinOnce(100);
{
// boost::lock_guard<MutexType> lock(safe_update);
MutexType::scoped_lock lock(safe_update);
if(has_to_update) {
if(!viewer->updatePointCloud<PointType>(cloud, "sample cloud")) {
viewer->addPointCloud<PointType>(cloud, "sample cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud");
viewer->resetCamera();
}
has_to_update = false;
}
} // end scoped_lock
stopped = viewer->wasStopped();
qDebug() << "Before a new loop - it:" << iteration << "\n";
}
delete viewer;
};
Well, Before spinOnce is only displayed once, with iteration=0. The Before a new loop line is never printed.
On the other hand, the main thread keeps calculating and printing those points to the standard output until 'q' is inputted.
It seems that the visualization thread frozens in the viewer->spinOnce(100)
call. If instead of spinOnce(100)
I use the other visualization method, spin()
, nothing changes.
Maybe there's a data race in my code, but for much I keep checking it, I can't find the race myself.
NOTE: According to the PCL library doc, spinOnce(int time)
calls the interactor and updates the screen once, whereas spin()
calls the interactor and runs an internal loop.
EDIT #2: Today I tried to execute the code again in Ubuntu and resulted in a deadlock with the PCL visualizer. I added some volatile
keywords and a new loop check. Now it seems it goes well (at least it worked as expected, no wrong turns...). Here's the new version:
Global vars:
volatile bool has_to_update(true); // as suggested by @daramarak
volatile bool quit(false); // new while loop control var
Visualize method:
void Visualize(PointCloudType::Ptr cloud) {
pcl::visualization::PCLVisualizer* viewer = new pcl::visualization::PCLVisualizer("Vis in thread",true);
viewer->setBackgroundColor(1.0,0.0,0.0);
viewer->addPointCloud<PointType>(cloud, "sample cloud");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
viewer->resetCamera();
while(!viewer->wasStopped() && !quit ) {
viewer->spinOnce(100);
{
MutexType::scoped_lock lock(safe_update);
if(has_to_update) {
if(!viewer->updatePointCloud<PointType>(cloud, "sample cloud")) {
viewer->addPointCloud<PointType>(cloud, "sample cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud");
viewer->resetCamera();
}
has_to_update = false;
}
} // end scoped_lock
}
delete viewer;
};
Main function:
// everything the same until...
std::cin>>exit;
quit = (exit=='q');
// no more changes
I dont' like, however, the new control loop var hack. Isn't there a better way to know when to exit?. Right now, I can't realize any other way...