





















































OpenThreads is a lightweight, cross-platform thread API for OSG classes and applications. It supports the fundamental elements required by a multithreaded program, that is, the thread object (OpenThreads::Thread), the mutex for locking data that may be shared by different threads (OpenThreads::Mutex), barrier (OpenThreads::Barrier), and condition (OpenThreads::Condition). The latter two are often used for thread synchronization.
To create a new thread for certain purposes, we have to derive the OpenThreads::Thread base class and re-implement some of its virtual methods. There are also some global functions for conveniently handling threads and thread attributes, for example:
In this example, we will design a new thread with the OpenThreads library and use it to read characters from the standard input. At the same time, the main process, that is, the OSG viewer and rendering backend will try retrieving the input characters and displaying them on the screen with the osgText library. The entire program can only quit normally when the data thread and main process are both completed.
#include <osg/Geode>
#include <osgDB/ReadFile>
#include <osgText/Text>
#include <osgViewer/Viewer>
#include <iostream>
class DataReceiverThread : public OpenThreads::Thread
{
public:
static DataReceiverThread* instance()
{
static DataReceiverThread s_thread;
return &s_thread;
}
virtual int cancel();
virtual void run();
void addToContent( int ch );
bool getContent( std::string& str );
protected:
OpenThreads::Mutex _mutex;
std::string _content;
bool _done;
bool _dirty;
};
int DataReceiverThread::cancel()
{
_done = true;
while( isRunning() ) YieldCurrentThread();
return 0;
}
void DataReceiverThread::run()
{
_done = false;
_dirty = true;
do
{
YieldCurrentThread();
int ch = 0;
std::cin.get(ch);
switch (ch)
{
case 0: break; // We don't want '' to be added
case 9: _done = true; break; // ASCII code of Tab = 9
default: addToContent(ch); break;
}
} while( !_done );
}
void DataReceiverThread::addToContent( int ch )
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
_content += ch;
_dirty = true;
}
bool getContent( std::string& str )
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
if ( _dirty )
{
str += _content;
_dirty = false;
return true;
}
return false;
}
class UpdateTextCallback : public osg::Drawable::UpdateCallback { public: virtual void update( osg::NodeVisitor* nv, osg::Drawable* drawable ) { osgText::Text* text = static_cast<osgText::Text*>(drawable); if ( text ) { std::string str("# "); if ( DataReceiverThread::instance()->getContent(str) ) text->setText( str ); } } };
osg::ref_ptr<osgText::Text> text = new osgText::Text;
text->setFont( "fonts/arial.ttf" );
text->setAxisAlignment( osgText::TextBase::SCREEN );
text->setDataVariance( osg::Object::DYNAMIC );
text->setInitialBound(
osg::BoundingBox(osg::Vec3(), osg::Vec3(400.0f, 20.0f, 20.0f))
);
text->setUpdateCallback( new UpdateTextCallback );
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable( text.get() );
geode->getOrCreateStateSet()->setMode(
GL_LIGHTING, osg::StateAttribute::OFF );
osgViewer::Viewer viewer;
viewer.setSceneData( geode.get() );
viewer.setUpViewInWindow( 50, 50, 640, 480 );
DataReceiverThread::instance()->startThread();
viewer.run();
DataReceiverThread::instance()->cancel();
return 0;
It is very common that applications use separate threads to load huge files from disk or from the Local Area Network (LAN). Other applications use threads to continuously receive data from the network service and client computers, or user-defined input devices including GPS and radar signals, which is of great speed and efficiency. Extra data handling threads can even specify an affinity processor to work on, and thus make use of today's dual-core and quad-core CPUs.
The OpenThreads library provides a minimal and complete object-oriented thread interface for OSG developers, and even general C++ threading programmers. It is used by the osgViewer library to implement multithreaded scene updating, culling, and drawing, which is the secret of highly efficient rendering in OSG. Note here, that multithreaded rendering doesn't simply mean executing OpenGL calls in different threads because the related rendering context (HGLRC under Win32) is thread-specific. One OpenGL context can only be current in one thread (using wglMakeCurrent() function). Thus, one OSG rendering window which wraps only one OpenGL context will never be activated and accept OpenGL calls synchronously in multiple threads. It requires an accurate control of the threading model to make everything work well.