I spent a few days tracking down a crash I was having generally when a
terrapage terrain was paging. This is using OSG 3.1, when I finally
caught the corruption I found that both the DatabasePager thread and
the Draw thread were calling OSG_NOTIFY.
I wrote up a test that does little more than start up a bunch of
threads and print messages and found it too crashes. I am testing on
both Linux x86-64 gcc 4.7.2, and Windows 7 Visual Studio 10 and both
crash, though it crashes much quicker on Windows.
It will crash pretty quickly with the default (no arguments) on Windows.
The following arguments are working for me to crash on Linux on a 12
core (24 with hyperthreading), x86-64 system.
./ThreadedNotify 48 10000000 2 2
It has been pointed out that cout/cerr are thread safe, so it should
be possible to make osg::notify thread safe as well. In this program
it helps when I reduce the notification level, at least with the low
number of messages being printed it seems to be unlikely.
--
David Fries <[email protected]> PGP pub CB1EE8F0
http://fries.net/~david/
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#ifdef WIN32
#include <windows.h>
#endif
#include <osg/ref_ptr>
#include <osg/Notify>
#include <osg/Timer>
#include <OpenThreads/Thread>
#include <OpenThreads/Barrier>
using namespace std;
// When enabled send OSG errors to stderr and if Windows OutputDebugStringA.
class OutputSplitNotifyHandler : public osg::NotifyHandler
{
public:
void notify(osg::NotifySeverity severity, const char *message)
{
fputs(message, stderr);
#if WIN32
OutputDebugStringA(message);
#endif
}
};
// Exercise the code to here, but don't print anything because output can
// be slow.
class SilentNotifyHandler : public osg::NotifyHandler
{
public:
void notify(osg::NotifySeverity severity, const char *message)
{
// Sanity check string length instead of doing absolutly nothing with
// it. A bad length could indicate a corrupted buffer.
// Extra sanity check, make sure two runs of strlen come back with the
// same length.
int len = strlen(message);
if(!len || len > 100 || len != strlen(message))
{
//fprintf(stderr, "unexpected message length %d\n", len);
}
}
};
class Spam : public OpenThreads::Thread
{
public:
Spam() : Count(0), Test(0) {}
virtual void run();
void SetCount(int c) { Count = c; }
void SetTest(int t) { Test = t; }
private:
int Count;
int Test;
};
void Spam::run()
{
stringstream ss;
ss << setw(3) << getThreadId() << " running output concurrency test ";
string base = ss.str();
for(int i=0; i<Count; ++i)
{
switch(Test)
{
case 0:
cout << base << "cout " << i << endl;
break;
case 1:
printf("%sprintf %d\n", base.c_str(), i);
#ifdef WIN32
fflush(stdout); // normally not required on Unix
#endif
break;
case 2:
// printed with the default debug levels
OSG_FATAL << base << "OSG_FATAL " << i << endl;
break;
case 3:
// not printed with the default debug levels
OSG_DEBUG << base << "OSG_DEBUG " << i << endl;
break;
}
}
}
int main(int argc, char **argv)
{
int i;
int tcount = 8;
int operations = 5000;
int test = 2;
// Test crashes more reliably if it isn't waiting on printing the messages.
int osg_handler = 2;
if(argc != 1 && argc != 4 && argc != 5)
{
cerr << "Usage: " << argv[0] << " threads count test [ osg_handler ]\n";
cerr << "test:\n"
"\t0 cout\n"
"\t1 printf\n"
"\t2 OSG_FATAL (prints)\n"
"\t3 OSG_DEBUG (not output)\n"
"osg_handler:\n"
"\t0 default OSG output notify handler\n"
"\t1 OutputSplitNotifyHandler\n"
"\t2 SilentNotifyHandler\n";
cerr << "While terrain is paging the draw thread and DatabasePager "
"thread were both writing to OSG_NOTIFY and one string found "
"writing over a TextureMapSet entry in OSG 3.1.0. This checks "
"for output concurrency issues.\n";
exit(1);
}
if(argc >= 4)
{
tcount = atoi(argv[1]);
operations = atoi(argv[2]);
test = atoi(argv[3]);
}
if(argc == 5)
{
osg_handler = atoi(argv[4]);
}
// Select OSG output method
osg::ref_ptr<osg::NotifyHandler> outputNotify;
switch(osg_handler)
{
case 0:
// keep default handler
break;
case 1:
outputNotify = new OutputSplitNotifyHandler;
break;
case 2:
outputNotify = new SilentNotifyHandler;
break;
default:
cerr << "osg_handler " << osg_handler << " not recognized\n";
exit(1);
}
if(outputNotify)
{
// Calling get is required to initialize the stream before setting the
// handler.
osg::getNotifyHandler();
osg::setNotifyHandler(outputNotify);
}
Spam *t = new Spam[tcount];
osg::Timer timer;
for(i=0; i<tcount; ++i)
{
t[i].SetCount(operations);
t[i].SetTest(test);
t[i].start();
}
for(i=0; i<tcount; ++i)
{
t[i].join();
}
cout << tcount << '\t' << timer.time_s() << endl;
delete [] t;
return 0;
}
_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org