Hi,

MSVC not support thread safe initialization of static variables at function scope.

GCC by default generate thread safe code, but it can generate code like MSVC by passing -fno-threadsafe-statics option.

So you can reproduce crash if add -fno-threadsafe-statics option to CXX_FLAGS.

Problem will reproduced very easy.

Mikhail.

05.06.2012 13:31, Mikhail I. Izmestev написал:
Hi Robert,

I tried to reproduce this problem on linux but without luck.

gcc use more complex method of initialization of static variables at
function scope.

So problem is only with MSVC 2008 (not sure about newer versions).

Mikhail.

05.06.2012 10:39, Mikhail I. Izmestev написал:
Hi,

Here is code to reproduce crash:

<code>
#include <osgDB/ReadFile>
#include <OpenThreads/Mutex>
#include <OpenThreads/Thread>
#include <OpenThreads/Condition>

static OpenThreads::Condition s_cond;
static OpenThreads::Mutex s_mtx;

class ReadThread : public OpenThreads::Thread
{
public:
ReadThread() {}

void run()
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_mtx);
s_cond.wait(&s_mtx);
}
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("cow.osg");
}
};

int main()
{
ReadThread t1, t2;
t1.start();
t2.start();

// OpenThreads::Thread::microSleep(500);

{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_mtx);
s_cond.broadcast();
}

t1.join();
t2.join();
return 0;
}
</code>

To reproduce you can use this script:
<code>
#!/usr/bin/env bash

while(true); do
./test
done
</code>

But while testing fix I have new crash at:
ntdll!RtlReportCriticalFailure+0x62
ntdll!RtlpReportHeapFailure+0x26
ntdll!RtlpHeapHandleError+0x12
ntdll!RtlpLogHeapFailure+0xa4
ntdll!RtlpAnalyzeHeapFailure+0x3a8
ntdll!RtlpFreeHeap+0x141f
ntdll!RtlFreeHeap+0x1a6
kernel32!HeapFree+0xa
MSVCR90!free+0x1c
MSVCP90!std::basic_string<char,std::char_traits<char>,std::allocator<char>

MSVCP90!std::basic_string<char,std::char_traits<char>,std::allocator<char>

MSVCP90!std::operator+<char,std::char_traits<char>,std::allocator<char>
osg92_osgDB!osgDB::Registry::createLibraryNameForExtension+0x34b
osg92_osgDB!osgDB::Registry::createLibraryNameForFile+0x42
osg92_osgDB!osgDB::Registry::read+0x1608
osg92_osgDB!osgDB::Registry::readImplementation+0x34c
osg92_osgDB!osgDB::Registry::readNodeImplementation+0x6a
osg92_osgDB!osgDB::Registry::readNode+0xfc
osg92_osgDB!osgDB::readNodeFile+0x4e
dotosgcrash!ReadThread::run+0x79

code:
std::string Registry::createLibraryNameForExtension(const std::string&
ext)
{
std::string lowercase_ext;
for(std::string::const_iterator sitr=ext.begin();
sitr!=ext.end();
++sitr)
{
lowercase_ext.push_back(tolower(*sitr));
}

ExtensionAliasMap::iterator itr=_extAliasMap.find(lowercase_ext);
if (itr!=_extAliasMap.end() && ext != itr->second) return
createLibraryNameForExtension(itr->second);

#if defined(OSG_JAVA_BUILD)
static std::string prepend =
std::string("osgPlugins-")+std::string(osgGetVersion())+std::string("/java");


#else
static std::string prepend =
std::string("osgPlugins-")+std::string(osgGetVersion())+std::string("/");
#endif
[..]

same design:
static std::string prepend =
std::string("osgPlugins-")+std::string(osgGetVersion())+std::string("/");

Mikhail.

04.06.2012 13:17, Mikhail I. Izmestev написал:
Hi Robert,

It is hard to reproduce this problem (as any thread safety problem) in
same state as previous.

I have another crash with same problem at:
ntdll!RtlEnterCriticalSection+0x6
ot12_OpenThreads!OpenThreads::Mutex::lock+0xe
osgdb_deprecated_osg!initGLNames+0x66
osgdb_deprecated_osg!StateSet_readLocalData+0x4f
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObjectOfType+0x9eb

osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObjectOfType+0x28
osgdb_deprecated_osg!Node_readLocalData+0x349
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readNode+0xe1
osgdb_osg!OSGReaderWriter::readNode+0xfe
osgdb_osg!OSGReaderWriter::readNode+0x31c
osg80_osgDB!osgDB::Registry::ReadNodeFunctor::doRead+0x28
osg80_osgDB!osgDB::Registry::read+0x5e4
osg80_osgDB!osgDB::Registry::readImplementation+0x34c
osg80_osgDB!osgDB::Registry::readNodeImplementation+0x6a
osg80_osgDB!osgDB::Registry::readNode+0xfc
osg80_osgDB!osgDB::readNodeFile+0x4e

crashed code:
int Mutex::lock() {
Win32MutexPrivateData *pd =
static_cast<Win32MutexPrivateData*>(_prvData);

#ifdef USE_CRITICAL_SECTION

// Block until we can take this lock.
EnterCriticalSection( &(pd->_cs) ); <<<<<<<<<<<< CRASH HERE

return 0;
[...]

pd->_cs == NULL

in this case problem at
void initGLNames()
{
static bool first_time = true;
if (!first_time) return;

static OpenThreads::Mutex s_initGLNames;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_initGLNames);

[...]

There is lot of other places with same design:
AnimationPath.cpp:201: static osg::ref_ptr<osg::AnimationPath> s_path =
new osg::AnimationPath;
CoordinateSystemNode.cpp:48: static ref_ptr<EllipsoidModel>
s_ellipsoidModel = new EllipsoidModel;
Drawable.cpp:30: static ref_ptr<StateSet> s_drawstate = new
osg::StateSet;
Node.cpp:77: static ref_ptr<StateSet> s_drawstate = new osg::StateSet;
Node.cpp:85: static ref_ptr<NodeCallback> s_nodecallback = new
osg::NodeCallback;
NodeCallback.cpp:31: static osg::ref_ptr<NodeCallback> s_nc = new
NodeCallback;
OccluderNode.cpp:30: static ref_ptr<ConvexPlanarOccluder> s_occluder =
new ConvexPlanarOccluder;
Sequence.cpp:26:static bool Sequence_matchLoopMode(const char* str,
Sequence.cpp:45:static const char*
Sequence_getLoopMode(Sequence::LoopMode mode)
Sequence.cpp:57:static bool Sequence_matchSeqMode(const char* str,
Sequence.cpp:76:static const char*
Sequence_getSeqMode(Sequence::SequenceMode mode)
StateAttribute.cpp:32: static ref_ptr<StateAttributeCallback> s_callback
= new osg::StateAttributeCallback;
StateSet.cpp:80: static bool first_time = true;
StateSet.cpp:83: static OpenThreads::Mutex s_initGLNames;
StateSet.cpp:362: static ref_ptr<StateSet::Callback> s_callback = new
osg::StateSet::Callback;
Uniform.cpp:212: static ref_ptr<Uniform::Callback> s_callback = new
osg::Uniform::Callback;

Mikhail.

04.06.2012 12:44, Robert Osfield написал:
Hi Mikhail,

I have moved the static into the global scope of the
osgWrappers/deprecated-dotosg/Drawable.cpp, file attached, could try
it out?

Robert.

On 2 June 2012 11:12, Mikhail I. Izmestev<[email protected]> wrote:
Hi,

I noticed crashing in dotosg wrappers when try to use
osgDB::readNodeFile
from multiple threads.
Crash occurs when I try to load .osg file from two threads.

For example callstack of crashed thread:
osg80_osgDB!concrete_wrapper::matches+0x4
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObjectOfType+0x527



osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObjectOfType+0x28


osgdb_deprecated_osg!Drawable_readLocalData+0xa1
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readDrawable+0xe1
osgdb_deprecated_osg!Geode_readLocalData+0x90
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readNode+0xe1
osgdb_deprecated_osg!Group_readLocalData+0x71
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readNode+0xe1
osgdb_deprecated_osg!Group_readLocalData+0x71
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readNode+0xe1
osgdb_osg!OSGReaderWriter::readNode+0xfe
osgdb_osg!OSGReaderWriter::readNode+0x31c
osg80_osgDB!osgDB::Registry::ReadNodeFunctor::doRead+0x28
osg80_osgDB!osgDB::Registry::read+0x5e4
osg80_osgDB!osgDB::Registry::readImplementation+0x34c
osg80_osgDB!osgDB::Registry::readNodeImplementation+0x6a
osg80_osgDB!osgDB::Registry::readNode+0xfc
osg80_osgDB!osgDB::readNodeFile+0x4e
[...]

second thread callstack:
msvcp90!std::basic_istream<char,std::char_traits<char>
msvcp90!std::basic_istream<char,std::char_traits<char>
osg80_osgDB!osgDB::FieldReader::_readField+0x3f8
osg80_osgDB!osgDB::FieldReaderIterator::field+0x19d
osgdb_deprecated_osg!Array_readLocalData+0xbfb
osgdb_deprecated_osg!Geometry_readLocalData+0x991
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readDrawable+0xe1
osgdb_deprecated_osg!Geode_readLocalData+0x90
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readNode+0xe1
osgdb_deprecated_osg!Group_readLocalData+0x71
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readNode+0xe1
osgdb_deprecated_osg!Group_readLocalData+0x9a
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readNode+0xe1
osgdb_deprecated_osg!Group_readLocalData+0x9a
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readNode+0xe1
osgdb_deprecated_osg!Group_readLocalData+0x71
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readObject+0x8fb
osg80_osgDB!osgDB::DeprecatedDotOsgWrapperManager::readNode+0xe1
osgdb_osg!OSGReaderWriter::readNode+0xfe
osgdb_osg!OSGReaderWriter::readNode+0x31c
osg80_osgDB!osgDB::Registry::ReadNodeFunctor::doRead+0x28
osg80_osgDB!osgDB::Registry::read+0x5e4
osg80_osgDB!osgDB::Registry::readImplementation+0x34c
osg80_osgDB!osgDB::Registry::readNodeImplementation+0x6a
osg80_osgDB!osgDB::Registry::readNode+0xfc
osg80_osgDB!osgDB::readNodeFile+0x4e
[...]

code:
struct concrete_wrapper: basic_type_wrapper
{
virtual ~concrete_wrapper() {}
concrete_wrapper(const osg::Object *myobj) : myobj_(myobj) {}
bool matches(const osg::Object *proto) const
{
return myobj_->isSameKindAs(proto);<<<<<<<<<<<<<<<<<CRASH
HERE<<<<<<<
}
const osg::Object *myobj_;
};

crash occurs because myobj_ is NULL.

myobj_ passed from osgdb_deprecated_osg!Drawable_readLocalData as
s_drawstate

bool Drawable_readLocalData(Object& obj, Input& fr)
{
bool iteratorAdvanced = false;

Drawable& drawable = static_cast<Drawable&>(obj);

static ref_ptr<StateSet> s_drawstate = new osg::StateSet;
if (StateSet* readState =
static_cast<StateSet*>(fr.readObjectOfType(*s_drawstate)))
{
drawable.setStateSet(readState);
iteratorAdvanced = true;
}
[...]


so problem is in
static ref_ptr<StateSet> s_drawstate = new osg::StateSet;

MSVC 2008 x64 compiler (maybe gcc too) generate this code as:

static bool s_drawstate_init = false;
static ref_ptr<StateSet> s_drawstate = NULL;

bool Drawable_readLocalData(Object& obj, Input& fr)
{
bool iteratorAdvanced = false;

Drawable& drawable = static_cast<Drawable&>(obj);

if(!s_drawstate_init)
{
s_drawstate_init = true;
s_drawstate = new osg::StateSet;
}

if (StateSet* readState =
static_cast<StateSet*>(fr.readObjectOfType(*s_drawstate)))
{
drawable.setStateSet(readState);
iteratorAdvanced = true;
}
[...]


and this code is not thread safe as you can see.

Possible solution is to move static variables to global scope to
avoid lazy
initialization.


Mikhail.
_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org





_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org







_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to