/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #include using namespace osg; using namespace OpenThreads; struct ThreadExitTidyUp { ThreadExitTidyUp(osg::GraphicsContext* context, bool closeContextOnExit): _context(context), _closeContextOnExit(closeContextOnExit) { osg::notify(osg::INFO)<<"starting thread context "<<_context<closeImplementation(); osg::notify(osg::INFO)<<" - done close context "<<_context<releaseContext(); } } } osg::GraphicsContext* _context; bool _closeContextOnExit; }; struct BlockOperation : public GraphicsOperation, public Block { BlockOperation(): GraphicsOperation("Block",false) { reset(); } virtual void release() { Block::release(); } virtual void operator () (GraphicsContext*) { glFlush(); Block::release(); } }; GraphicsThread::GraphicsThread(): _graphicsContext(0), _done(false) { _operationsBlock = new Block; } GraphicsThread::~GraphicsThread() { //osg::notify(osg::NOTICE)<<"Destructing graphics thread "< lock(_operationsMutex); if (_currentOperation.valid()) { osg::notify(osg::INFO)<<"releasing "<<_currentOperation.get()<release(); } } _operationsBlock->release(); } } int GraphicsThread::cancel() { osg::notify(osg::INFO)<<"Cancelling graphics thread "<release(); } // release the frameBlock and _databasePagerThreadBlock incase its holding up thread cancelation. _operationsBlock->release(); // then wait for the the thread to stop running. while(isRunning()) { _operationsBlock->release(); { OpenThreads::ScopedLock lock(_operationsMutex); for(OperationQueue::iterator itr = _operations.begin(); itr != _operations.end(); ++itr) { (*itr)->release(); } if (_currentOperation.valid()) _currentOperation->release(); } // commenting out debug info as it was cashing crash on exit, presumable // due to osg::notify or std::cout destructing earlier than this destructor. osg::notify(osg::INFO)<<" Waiting for GraphicsThread to cancel "< block = 0; { // aquire the lock on the operations queue to prevent anyone else for modifying it at the same time OpenThreads::ScopedLock lock(_operationsMutex); // add the operation to the end of the list _operations.push_back(operation); if (waitForCompletion) { block = new BlockOperation; _operations.push_back(block.get()); } _operationsBlock->set(true); } if (block.valid()) { // now we wait till the barrier is joined by the graphics thread. block->block(); } } void GraphicsThread::remove(GraphicsOperation* operation) { osg::notify(osg::INFO)<<"Doing remove operation"< lock(_operationsMutex); for(OperationQueue::iterator itr = _operations.begin(); itr!=_operations.end();) { if ((*itr)==operation) itr = _operations.erase(itr); else ++itr; } } void GraphicsThread::remove(const std::string& name) { osg::notify(osg::INFO)<<"Doing remove named operation"< lock(_operationsMutex); // find the remove all operations with specificed name for(OperationQueue::iterator itr = _operations.begin(); itr!=_operations.end();) { if ((*itr)->getName()==name) itr = _operations.erase(itr); else ++itr; } if (_operations.empty()) { _operationsBlock->set(false); } } void GraphicsThread::removeAllOperations() { osg::notify(osg::INFO)<<"Doing remove all operations"< lock(_operationsMutex); _operations.clear(); if (_operations.empty()) { _operationsBlock->set(false); } } void GraphicsThread::run() { bool contextRealizedInThisThread = false; // make the graphics context current. if (_graphicsContext) { if (!_graphicsContext->isRealized()) { #if 0 osg::notify(osg::NOTICE)<<"Forced to do a realize in GraphicsThread::run."<realize(); contextRealizedInThisThread = true; #else while (!_graphicsContext->isRealized() && !_done) { osg::notify(osg::INFO)<<"Waiting in GraphicsThread::run for GraphicsContext to realize."<makeCurrentImplementation(); } // _graphicsContext->makeCurrentImplementation(); // create a local object to clean up once the thread is cancelled. ThreadExitTidyUp threadExitTypeUp(_graphicsContext, contextRealizedInThisThread); osg::notify(osg::INFO)<<"Doing run "<block(); // exit from loop if _done is set. if (_done) break; itr = _operations.begin(); } else { if (itr == _operations.end()) itr = _operations.begin(); } osg::notify(osg::INFO)<<"get op "<<_done<<" "< lock(_operationsMutex); if (!_operations.empty()) { // get the next item _currentOperation = *itr; if (!_currentOperation->getKeep()) { osg::notify(osg::INFO)<<"removing "<<_currentOperation->getName()<set(false); } } else { osg::notify(osg::INFO)<<"increment "<<_currentOperation->getName()<getName()<<" "< lock(_operationsMutex); _currentOperation = 0; } } if (firstTime) { // do a yield to get round a peculiar thread hang when testCancel() is called // in certain cirumstances - of which there is no particular pattern. YieldCurrentThread(); firstTime = false; } // osg::notify(osg::NOTICE)<<"operations.size()="<<_operations.size()<<" done="<<_done<<" testCancel()"<releaseContext(); osg::notify(osg::INFO)<<"exit loop "<