Related
I need to handle include directives, similar to standard C/C++ preprocessor.
I already found this solution (in C#), based on changing lexer input stream: C# and ANTLR4: Handling "include" directives when parsing a file so I did the same in C++, but I get a segfault in antlr4 CPP runtime library.
Here is the very basic reproducer, based on CPP runtime demo provided example:
Modify parsed string to embbed an include directive:
diff --git a/runtime/Cpp/demo/Linux/main.cpp b/runtime/Cpp/demo/Linux/main.cpp
index 672ce2a3b..4e44347fb 100644
--- a/runtime/Cpp/demo/Linux/main.cpp
+++ b/runtime/Cpp/demo/Linux/main.cpp
## -20,7 +20,7 ## using namespace antlrcpptest;
using namespace antlr4;
int main(int , const char **) {
- ANTLRInputStream input(u8"๐ด = ๐ + \"๐\";(((x * ฯ))) * ยต + โฐ; a + (x * (y ? 0 : 1) + z);");
+ ANTLRInputStream input(u8"๐ด = ๐ + \"๐\"; #include \"test.txt\"");
TLexer lexer(&input);
CommonTokenStream tokens(&lexer);
Create the included file that contains the rest of the string:
$ cat test.txt
(((x * ฯ))) * ยต + โฐ; a + (x * (y ? 0 : 1) + z);
Add support for include directive in the provided lexer:
diff --git a/runtime/Cpp/demo/TLexer.g4 b/runtime/Cpp/demo/TLexer.g4
index ac2128c8d..26d70e4ea 100644
--- a/runtime/Cpp/demo/TLexer.g4
+++ b/runtime/Cpp/demo/TLexer.g4
## -3,7 +3,10 ## lexer grammar TLexer;
// These are all supported lexer sections:
// Lexer file header. Appears at the top of h + cpp files. Use e.g. for copyrights.
-#lexer::header {/* lexer header section */}
+#lexer::header {/* lexer header section */
+ #include <iostream>
+ #include <stack>
+}
// Appears before any #include in h + cpp files.
#lexer::preinclude {/* lexer precinclude section */}
## -21,6 +24,16 ## lexer grammar TLexer;
// Appears in the public part of the lexer in the h file.
#lexer::members {/* public lexer declarations section */
+std::stack<antlr4::CharStream *> input_stack;
+virtual antlr4::Token *emitEOF() override {
+ if (input_stack.empty()) {
+ return Lexer::emitEOF();
+ };
+ hitEOF = false;
+ setInputStream(input_stack.top());
+ input_stack.pop();
+ return nextToken().get();
+}
bool canTestFoo() { return true; }
bool isItFoo() { return true; }
bool isItBar() { return true; }
## -69,6 +82,12 ## Comma: ',' -> skip;
Dollar: '$' -> more, mode(Mode1);
Ampersand: '&' -> type(DUMMY);
+fragment SPACES : [ \t]+ ;
+INCLUDE : '#include' SPACES '"' {
+ std::cerr << "Got include directive " << getSourceName() << "\n";
+ std::cerr << "Current mode = " << mode << "\n";
+ } -> skip, pushMode(INCLUDEHANLDING);
+
String: '"' .*? '"';
Foo: {canTestFoo()}? 'foo' {isItFoo()}? { myFooLexerAction(); };
Bar: 'bar' {isItBar()}? { myBarLexerAction(); };
## -84,3 +103,36 ## Dot: '.';
mode Mode2;
DotDot: '..';
+
+mode INCLUDEHANLDING;
+// Skipped to hide FILE token to parser
+FILE : ~["]+ {
+ {
+ // Create new input stream from the file mentioned
+ std::ifstream stream(getText());
+ if (stream.fail()) {
+ std::cerr << "Config error: " << std::strerror(errno) << " for "<< getText() << "\n";
+ } else {
+ // Push the old stream to stack
+ input_stack.push(getInputStream());
+ std::cerr << "Handling open file. mode = " << mode << "\n";
+ // This new stream will be popped and used right after, on DQUOTE.
+ input_stack.push(new ANTLRInputStream(stream));
+ }
+ }
+} -> skip;
+
+// Skipped to hide DQUOTE token to parser
+DQUOTE: '"' {
+ // Injecting the newly generated Stream.
+ std::cerr << "Current mode = " << mode << "\n";
+ setInputStream(input_stack.top());
+ input_stack.pop();
+ std::cerr << "Injected stream. Now reading from " << getSourceName() << "\n";
+ std::cerr << "Current mode = " << mode << "\n";
+} -> skip;
+
+NL : ('\r'? '\n' | '\r')+ -> skip;
Recompile the demo example
make
Run it and you get a segfault:
Got include directive <unknown>
Current mode = 0
Handling open file. mode = 3
Current mode = 3
Injected stream. Now reading from <unknown>
Current mode = 0
Segmentation fault (core dumped)
Debugging with gdb:
I recompiled the runtime library with debug support.
gdb demo/antlr4-demo
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo/antlr4-demo...
(gdb) r
Starting program: /home/adrpes01/work/antlr/antlr4/runtime/Cpp/build/demo/antlr4-demo
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Got include directive <unknown>
Current mode = 0
Handling open file. mode = 3
Current mode = 3
Injected stream. Now reading from <unknown>
Current mode = 0
Program received signal SIGSEGV, Segmentation fault.
antlr4::atn::LexerATNSimulator::failOrAccept (this=0x55555570c7b0, input=0x7fffffffd9b0, reach=0x555555712fc0, t=18446744073709551615)
at /home/adrpes01/work/antlr/antlr4/runtime/Cpp/runtime/src/atn/LexerATNSimulator.cpp:213
213 return _prevAccept.dfaState->prediction;
(gdb) where
#0 antlr4::atn::LexerATNSimulator::failOrAccept (this=0x55555570c7b0, input=0x7fffffffd9b0, reach=0x555555712fc0, t=18446744073709551615)
at /home/adrpes01/work/antlr/antlr4/runtime/Cpp/runtime/src/atn/LexerATNSimulator.cpp:213
#1 0x00005555555ca9d9 in antlr4::atn::LexerATNSimulator::execATN (this=0x55555570c7b0, input=0x7fffffffd9b0, ds0=0x555555712ab0)
at /home/adrpes01/work/antlr/antlr4/runtime/Cpp/runtime/src/atn/LexerATNSimulator.cpp:167
#2 0x00005555555ca58c in antlr4::atn::LexerATNSimulator::match (this=0x55555570c7b0, input=0x7fffffffd9b0, mode=3)
at /home/adrpes01/work/antlr/antlr4/runtime/Cpp/runtime/src/atn/LexerATNSimulator.cpp:76
#3 0x000055555558ea88 in antlr4::Lexer::nextToken (this=0x7fffffffdb00) at /home/adrpes01/work/antlr/antlr4/runtime/Cpp/runtime/src/Lexer.cpp:80
#4 0x00005555555887d4 in antlr4::BufferedTokenStream::fetch (this=0x7fffffffd950, n=1000)
at /home/adrpes01/work/antlr/antlr4/runtime/Cpp/runtime/src/BufferedTokenStream.cpp:96
#5 0x000055555558a812 in antlr4::BufferedTokenStream::fill (this=0x7fffffffd950)
at /home/adrpes01/work/antlr/antlr4/runtime/Cpp/runtime/src/BufferedTokenStream.cpp:404
#6 0x000055555556a400 in main () at /home/adrpes01/work/antlr/antlr4/runtime/Cpp/demo/Linux/main.cpp:27
(gdb) l
208 }
209
210 size_t LexerATNSimulator::failOrAccept(CharStream *input, ATNConfigSet *reach, size_t t) {
211 if (_prevAccept.dfaState != nullptr) {
212 accept(input, _prevAccept.dfaState->lexerActionExecutor, _startIndex, _prevAccept.index, _prevAccept.line, _prevAccept.charPos);
213 return _prevAccept.dfaState->prediction;
214 } else {
215 // if no accept and EOF is first char, return EOF
216 if (t == Token::EOF && input->index() == _startIndex) {
217 return Token::EOF;
(gdb) p _prevAccept
$1 = {index = 18446744073709551615, line = 0, charPos = 18446744073709551615, dfaState = 0x0}
(gdb)
Crash occurs at /home/adrpes01/work/antlr/antlr4/runtime/Cpp/runtime/src/atn/LexerATNSimulator.cpp:213
213 return _prevAccept.dfaState->prediction;
... whish is weird, since _prevAccept.dfaState effectively seems null:
(gdb) p _prevAccept
$1 = {index = 18446744073709551615, line = 0, charPos = 18446744073709551615, dfaState = 0x0}
From what I understood, Lexer::reset() calls getInterpreteratn::LexerATNSimulator()->reset(), that itself call _prevAccept.reset();
Question is:
Am I doing something wrong that mess up ANTLR internal ?
Am I not doing soemthing that I should do ?
Or is it a real bug from CPP runtime ?
Thanks for your help
I'm working on a simple class which upon creation schedules a periodic timer for invoking one of its' methods. The method is virtual, so that derived classes can overload it with whatever periodic work they need.
In my test of this class, however, I randomly experience segmentation fault and can't figure out why. Here's the code and example of good and bad outputs:
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/chrono.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/function.hpp>
#include <boost/atomic.hpp>
#include <boost/make_shared.hpp>
#include <boost/bind.hpp>
//******************************************************************************
class PeriodicImpl;
class Periodic {
public:
Periodic(boost::asio::io_service& io, unsigned int periodMs);
~Periodic();
virtual unsigned int periodicInvocation() = 0;
private:
boost::shared_ptr<PeriodicImpl> pimpl_;
};
//******************************************************************************
class PeriodicImpl : public boost::enable_shared_from_this<PeriodicImpl>
{
public:
PeriodicImpl(boost::asio::io_service& io, unsigned int periodMs,
boost::function<unsigned int(void)> workFunc);
~PeriodicImpl();
void setupTimer(unsigned int intervalMs);
boost::atomic<bool> isRunning_;
unsigned int periodMs_;
boost::asio::io_service& io_;
boost::function<unsigned int(void)> workFunc_;
boost::asio::steady_timer timer_;
};
//******************************************************************************
Periodic::Periodic(boost::asio::io_service& io, unsigned int periodMs):
pimpl_(boost::make_shared<PeriodicImpl>(io, periodMs, boost::bind(&Periodic::periodicInvocation, this)))
{
std::cout << "periodic ctor " << pimpl_.use_count() << std::endl;
pimpl_->setupTimer(periodMs);
}
Periodic::~Periodic()
{
std::cout << "periodic dtor " << pimpl_.use_count() << std::endl;
pimpl_->isRunning_ = false;
pimpl_->timer_.cancel();
std::cout << "periodic dtor end " << pimpl_.use_count() << std::endl;
}
//******************************************************************************
PeriodicImpl::PeriodicImpl(boost::asio::io_service& io, unsigned int periodMs,
boost::function<unsigned int(void)> workFunc):
isRunning_(true),
io_(io), periodMs_(periodMs), workFunc_(workFunc), timer_(io_)
{
}
PeriodicImpl::~PeriodicImpl()
{
std::cout << "periodic impl dtor" << std::endl;
}
void
PeriodicImpl::setupTimer(unsigned int intervalMs)
{
std::cout << "schedule new " << intervalMs << std::endl;
boost::shared_ptr<PeriodicImpl> self(shared_from_this());
timer_.expires_from_now(boost::chrono::milliseconds(intervalMs));
timer_.async_wait([self, this](const boost::system::error_code& e){
std::cout << "hello invoke" << std::endl;
if (!e)
{
if (isRunning_)
{
std::cout << "invoking" << std::endl;
unsigned int nextIntervalMs = workFunc_();
if (nextIntervalMs)
setupTimer(nextIntervalMs);
}
else
std::cout << "invoke not running" << std::endl;
}
else
std::cout << "invoke cancel" << std::endl;
});
std::cout << "scheduled " << self.use_count() << std::endl;
}
//******************************************************************************
class PeriodicTest : public Periodic
{
public:
PeriodicTest(boost::asio::io_service& io, unsigned int periodMs):
Periodic(io, periodMs), periodMs_(periodMs), workCounter_(0){}
~PeriodicTest(){
std::cout << "periodic test dtor" << std::endl;
}
unsigned int periodicInvocation() {
std::cout << "invocation " << workCounter_ << std::endl;
workCounter_++;
return periodMs_;
}
unsigned int periodMs_;
unsigned int workCounter_;
};
//******************************************************************************
void main()
{
boost::asio::io_service io;
boost::shared_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(io));
boost::thread t([&io](){
io.run();
});
unsigned int workCounter = 0;
{
PeriodicTest p(io, 50);
boost::this_thread::sleep_for(boost::chrono::milliseconds(550));
workCounter = p.workCounter_;
}
work.reset();
//EXPECT_EQ(10, workCounter);
}
Good output:
hello invoke
invoking
invocation 9
schedule new 50
scheduled 5
periodic test dtor
periodic dtor 2
periodic dtor end 2
hello invoke
invoke cancel
periodic impl dtor
Bad output:
hello invoke
invoking
invocation 9
schedule new 50
scheduled 5
periodic test dtor
periodic dtor 2
periodic dtor end 2
periodic impl dtor
Segmentation fault: 11
Apparently, segmentation fault is happening because PeriodicImpl is destructed so as its' timer timer_. But timer is still scheduled - and this leads to SEGFAULT. I can't understand why PeriodicImpl destructor is called in this case, because a shared_ptr to PeriodicImpl was copied to lambda passed as the timer's handler function during setupTimer call and this should've retained a copy of PeriodicImpl and prevent destructor invocation.
Any ideas?
The problem turned out to be entirely not in the questioned code, but in the code that tested it.
I enabled saving core dump file by running ulimit -c unlimited and then used lldb to read it:
$ lldb bin/tests/test-segment-controller -c /cores/core.75876
(lldb) bt all
* thread #1: tid = 0x0000, 0x00007fff8eb800f9 libsystem_malloc. dylib`szone_malloc_should_clear + 2642, stop reason = signal SIGSTOP
* frame #0: 0x00007fff8eb800f9 libsystem_malloc.dylib`szone_malloc_should_clear + 2642
frame #1: 0x00007fff8eb7f667 libsystem_malloc.dylib`malloc_zone_malloc + 71
frame #2: 0x00007fff8eb7e187 libsystem_malloc.dylib`malloc + 42
frame #3: 0x00007fff9569923e libc++abi.dylib`operator new(unsigned long) + 30
frame #4: 0x000000010da4b516 test-periodic`testing::Message::Message( this=0x00007fff521e8450) + 38 at gtest.cc:946
frame #5: 0x000000010da4a645 test-periodic`testing::Message::Message( this=0x00007fff521e8450) + 21 at gtest.cc:946
frame #6: 0x000000010da6c027 test-periodic`std::string testing::internal::StreamableToString<long long>(streamable=0x00007fff521e84b0) + 39 at gtest-message.h:244
frame #7: 0x000000010da558e8 test- periodic`testing::internal::PrettyUnitTestResultPrinter::OnTestEnd( this=0x00007fe733421570, test_info=0x00007fe7334211c0) + 216 at gtest.cc:3141
frame #8: 0x000000010da56a28 test- periodic`testing::internal::TestEventRepeater::OnTestEnd( this=0x00007fe733421520, parameter=0x00007fe7334211c0) + 136 at gtest.cc:3321
frame #9: 0x000000010da53957 test-periodic`testing::TestInfo::Run( this=0x00007fe7334211c0) + 343 at gtest.cc:2667
frame #10: 0x000000010da540c7 test-periodic`testing::TestCase::Run( this=0x00007fe733421660) + 231 at gtest.cc:2774
frame #11: 0x000000010da5b5d6 test- periodic`testing::internal::UnitTestImpl::RunAllTests(this=0x00007fe733421310) + 726 at gtest.cc:4649
frame #12: 0x000000010da83263 test-periodic`bool testing::internal::HandleSehExceptionsInMethodIfSupported< testing::internal::UnitTestImpl, bool>(object=0x00007fe733421310, method=0x000000010da5b300, location="auxiliary test code (environments or event listeners)")(), char const*) + 131 at gtest.cc:2402
frame #13: 0x000000010da6cde1 test-periodic`bool testing::internal::HandleExceptionsInMethodIfSupported< testing::internal::UnitTestImpl, bool>(object=0x00007fe733421310, method=0x000000010da5b300, location="auxiliary test code (environments or event listeners)")(), char const*) + 113 at gtest.cc:2438
frame #14: 0x000000010da5b2a2 test-periodic`testing::UnitTest::Run( this=0x000000010dab18e8) + 210 at gtest.cc:4257
frame #15: 0x000000010da19541 test-periodic`RUN_ALL_TESTS() + 17 at gtest. h:2233
frame #16: 0x000000010da1818b test-periodic`main(argc=1, argv=0x00007fff521e88b8) + 43 at test-periodic.cc:57
frame #17: 0x00007fff9557b5c9 libdyld.dylib`start + 1
frame #18: 0x00007fff9557b5c9 libdyld.dylib`start + 1
thread #2: tid = 0x0001, 0x00007fff8ab404cd libsystem_pthread. dylib`_pthread_mutex_lock + 23, stop reason = signal SIGSTOP
frame #0: 0x00007fff8ab404cd libsystem_pthread.dylib`_pthread_mutex_lock + 23
frame #1: 0x000000010da1c8d5 test- periodic`boost::asio::detail::posix_mutex::lock(this=0x0000000000000030) + 21 at posix_mutex.hpp:52
frame #2: 0x000000010da1c883 test-periodic`boost::asio::detail::scoped_lock< boost::asio::detail::posix_mutex>::scoped_lock(this=0x000000010e4fac38, m=0x0000000000000030) + 51 at scoped_lock.hpp:46
frame #3: 0x000000010da1c79d test-periodic`boost::asio::detail::scoped_lock< boost::asio::detail::posix_mutex>::scoped_lock(this=0x000000010e4fac38, m=0x0000000000000030) + 29 at scoped_lock.hpp:45
frame #4: 0x000000010da227a7 test- periodic`boost::asio::detail::kqueue_reactor::run(this=0x0000000000000000, block=true, ops=0x000000010e4fbda8) + 103 at kqueue_reactor.ipp:355
frame #5: 0x000000010da2223c test- periodic`boost::asio::detail::task_io_service::do_run_one( this=0x00007fe733421900, lock=0x000000010e4fbd60, this_thread=0x000000010e4fbd98, ec=0x000000010e4fbe58) + 348 at task_io_service .ipp:368
frame #6: 0x000000010da21e9f test- periodic`boost::asio::detail::task_io_service::run(this=0x00007fe733421900, ec=0x000000010e4fbe58) + 303 at task_io_service.ipp:153
frame #7: 0x000000010da21d51 test-periodic`boost::asio::io_service::run( this=0x00007fff521e8338) + 49 at io_service.ipp:59
frame #8: 0x000000010da184b8 test- periodic`TestPeriodic_TestDestructionDifferentThread_Test::TestBody( this=0x00007fe733421e28)::$_0::operator()() const + 24 at test-periodic.cc:41
frame #9: 0x000000010da1846c test-periodic`boost::detail::thread_data< TestPeriodic_TestDestructionDifferentThread_Test::TestBody()::$_0>::run( this=0x00007fe733421c10) + 28 at thread.hpp:117
frame #10: 0x000000010da8849c test-periodic`boost::(anonymous namespace) ::thread_proxy(param=<unavailable>) + 124 at thread.cpp:164
frame #11: 0x00007fff8ab4305a libsystem_pthread.dylib`_pthread_body + 131
frame #12: 0x00007fff8ab42fd7 libsystem_pthread.dylib`_pthread_start + 176
frame #13: 0x00007fff8ab403ed libsystem_pthread.dylib`thread_start + 13
Apparently, thread 2 causes crash as it tries to lock mutex which is already destructed. However, I'm not using any mutexes, so this must be something internal to io_service. This might happen if io_service is still being used after its' destruction. Looking closely at my main() function I noticed that the thread t I created is left dangling, i.e. there is no join() call on it. Consequently, this sometimes creates a situation when io object is already destructed (at the end of main) but thread t still tries to use it.
Thus, the problem was fixed by adding t.join() call at the end of main() function:
void main()
{
boost::asio::io_service io;
boost::shared_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(io));
boost::thread t([&io](){
io.run();
});
unsigned int workCounter = 0;
{
PeriodicTest p(io, 50);
boost::this_thread::sleep_for(boost::chrono::milliseconds(550));
workCounter = p.workCounter_;
}
work.reset();
t.join();
//EXPECT_EQ(10, workCounter);
}
I run your program.Regreattably,I falied to compile.
I add to your program,and modify the following code:
timer_.expires_from_now(boost::chrono::milliseconds(intervalMs));
Modified:
timer_.expires_from_now(std::chrono::milliseconds(intervalMs));
So,I get the same result as your "Good output",and don't get the same result as your "Bad output".
I am new to both C++ and ITK (and a newbie to stackoverflow as well). I have been fiddling with test code for learning purposes. The following is a simple piece of code to read in a grayscale image, threshold it to get a binary image, and get a label map out of it. I use MS VS2010 and ITK v4.5.1.
typedef itk::Image< unsigned char, 2 > ScalarImageType;
typedef itk::ImageFileWriter< ScalarImageType > WriterType;
typedef itk::ImageFileReader< ScalarImageType > ReaderType;
typedef itk::OtsuThresholdImageFilter< ScalarImageType, ScalarImageType > OtsuThresholdImageFilterType;
typedef itk::BinaryImageToLabelMapFilter< ScalarImageType > BinaryImageToLabelMapFilterType;
// read a grayscale image
ReaderType::Pointer reader = ReaderType::New();
reader->SetFileName("test_grey.jpg");
try
{
reader->Update();
}
catch( itk::ExceptionObject &err )
{
std::cerr << "ExceptionObject caught !" << std::endl;
std::cerr << err << std::endl;
return(FALSE);
}
// threshold the grayscale image
OtsuThresholdImageFilterType::Pointer otsuThresImgFilter = OtsuThresholdImageFilterType::New();
otsuThresImgFilter->SetInput(reader->GetOutput());
otsuThresImgFilter->SetInsideValue(255);
otsuThresImgFilter->SetOutsideValue(0);
try
{
otsuThresImgFilter->Update();
}
catch( itk::ExceptionObject &err )
{
std::cerr << "ExceptionObject caught !" << std::endl;
std::cerr << err << std::endl;
return(FALSE);
}
// get a label map from the binary image
BinaryImageToLabelMapFilterType::Pointer binarytoLabelmapFilter = BinaryImageToLabelMapFilterType::New();
binarytoLabelmapFilter->SetInput(otsuThresImgFilter->GetOutput());
try
{
binarytoLabelmapFilter->Update();
}
catch( itk::ExceptionObject &err )
{
std::cerr << "ExceptionObject caught !" << std::endl;
std::cerr << err << std::endl;
return(FALSE);
}
This works fine for all images, except those which have only 1 row and N columns (interestingly, 1 x 1 images go through fine).
So, for 1 x N images, the program crashes in release mode. In debug mode, I get the "vector subscript out of range" error. Stepping through the code, I found that the crash happens on the line binarytoLabelmapFilter->Update(); of my code.
Call stack:
msvcr100d.dll!_CrtDbgBreak() Line 85 C
msvcr100d.dll!_VCrtDbgReportW(int nRptType, const wchar_t * szFile, int nLine, const wchar_t * szModule, const wchar_t * szFormat, char * arglist) Line 502 C
msvcr100d.dll!_CrtDbgReportWV(int nRptType, const wchar_t * szFile, int nLine, const wchar_t * szModule, const wchar_t * szFormat, char * arglist) Line 241 + 0x1d bytes C++
msvcr100d.dll!_CrtDbgReportW(int nRptType, const wchar_t * szFile, int nLine, const wchar_t * szModule, const wchar_t * szFormat, ...) Line 258 + 0x1d bytes C++
msvcp100d.dll!std::_Debug_message(const wchar_t * message, const wchar_t * file, unsigned int line) Line 13 + 0x16 bytes C++
HelloWorld.exe!std::vector<std::vector<itk::BinaryImageToLabelMapFilter<itk::Image<unsigned char,2>,itk::LabelMap<itk::LabelObject<unsigned long,2> > >::runLength,std::allocator<itk::BinaryImageToLabelMapFilter<itk::Image<unsigned char,2>,itk::LabelMap<itk::LabelObject<unsigned long,2> > >::runLength> >,std::allocator<std::vector<itk::BinaryImageToLabelMapFilter<itk::Image<unsigned char,2>,itk::LabelMap<itk::LabelObject<unsigned long,2> > >::runLength,std::allocator<itk::BinaryImageToLabelMapFilter<itk::Image<unsigned char,2>,itk::LabelMap<itk::LabelObject<unsigned long,2> > >::runLength> > > >::operator[](unsigned int _Pos) Line 932 + 0x17 bytes C++
HelloWorld.exe!itk::BinaryImageToLabelMapFilter<itk::Image<unsigned char,2>,itk::LabelMap<itk::LabelObject<unsigned long,2> > >::ThreadedGenerateData(const itk::ImageRegion<2> & outputRegionForThread, unsigned int threadId) Line 190 + 0x1c bytes C++
HelloWorld.exe!itk::ImageSource<itk::LabelMap<itk::LabelObject<unsigned long,2> > >::ThreaderCallback(void * arg) Line 295 + 0x25 bytes C++
HelloWorld.exe!itk::MultiThreader::SingleMethodProxy(void * arg) Line 375 + 0xe bytes C++
msvcr100d.dll!_callthreadstartex() Line 314 + 0xf bytes C
msvcr100d.dll!_threadstartex(void * ptd) Line 297 C
kernel32.dll!773f338a()
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]
ntdll.dll!77a49f72()
ntdll.dll!77a49f45()
Any idea what's going on? Any tips on how to investigate further? Being a novice, I am not sure if it is an issue in ITK that I should raise as a bug or if it is due to something I am doing wrong.
The problem seems to relate to the number of threads used by BinaryImageToLabelMapFilter.
The error disappears if I set the number of threads to 1, by adding the line
binarytoLabelmapFilter->SetNumberofThreads(1);
before updating the filter.
It seems to be a bug in ITK. Discussion thread in the ITK mailing list - http://public.kitware.com/pipermail/community/2014-July/003110.html
Is static local variables safe for a class that creates/uses std::threads?
Because when I use something like this:
logger& logger::get_instance(void)
{
static logger lg;
return lg;
}
And try to exit (force close) the executable, it crashes/exits improperly (somethings the Visual Studio 2012 debugger even crashes).
When I don't do that, the program exits gracefully when i force close.
Here's the stack call when it crashes
ntdll.dll!77c10dbd() Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
ntdll.dll!77b7bfdc() Unknown
kernel32.dll!75b55bab() Unknown
> msvcr110d.dll!__crtCreateThreadpoolWait(void (_TP_CALLBACK_INSTANCE *, void *, _TP_WAIT *, unsigned long) * pfnwa, void * pv, _TP_CALLBACK_ENVIRON_V1 * pcbe) Line 569 C
msvcr110d.dll!Concurrency::details::RegisterAsyncWaitAndLoadLibrary(void * waitingEvent, void (_TP_CALLBACK_INSTANCE *, void *, _TP_WAIT *, unsigned long) * callback, void * data) Line 675 C++
msvcr110d.dll!Concurrency::details::ExternalContextBase::PrepareForUse(bool explicitAttach) Line 120 C++
msvcr110d.dll!Concurrency::details::ExternalContextBase::ExternalContextBase(Concurrency::details::SchedulerBase * pScheduler, bool explicitAttach) Line 52 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::GetExternalContext(bool explicitAttach) Line 1579 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::AttachExternalContext(bool explicitAttach) Line 1527 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler() Line 569 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 402 C++
msvcr110d.dll!Concurrency::details::LockQueueNode::LockQueueNode(unsigned int timeout) Line 616 C++
msvcr110d.dll!Concurrency::critical_section::lock() Line 1017 C++
msvcp110d.dll!mtx_do_lock(_Mtx_internal_imp_t * * mtx, const xtime * target) Line 65 C++
msvcp110d.dll!_Mtx_lock(_Mtx_internal_imp_t * * mtx) Line 144 C++
escobar.exe!std::_Mtx_lockX(_Mtx_internal_imp_t * * _Mtx) Line 68 C++
escobar.exe!std::_Mutex_base::lock() Line 43 C++
escobar.exe!std::unique_lock<std::mutex>::unique_lock<std::mutex>(std::mutex & _Mtx) Line 228 C++
escobar.exe!escobar::utilities::blocking_queue<escobar::logging::log_message *>::interrupt() Line 71 C++
escobar.exe!escobar::logging::log_worker::~log_worker() Line 17 C++
escobar.exe!escobar::logging::log_worker::`scalar deleting destructor'(unsigned int) C++
escobar.exe!escobar::logging::logger::close() Line 72 C++
escobar.exe!escobar::logging::logger::~logger() Line 27 C++
escobar.exe!`escobar::logging::logger::get_instance'::`2'::`dynamic atexit destructor for 'lg''() C++
msvcr110d.dll!doexit(int code, int quick, int retcaller) Line 585 C
msvcr110d.dll!_cexit() Line 410 C
msvcr110d.dll!__CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 296 C
msvcr110d.dll!_CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 210 C
ntdll.dll!77bb2846() Unknown
ntdll.dll!77bb2893() Unknown
ntdll.dll!77bc09c8() Unknown
ntdll.dll!77bc08ad() Unknown
KernelBase.dll!75525bbb() Unknown
KernelBase.dll!75525c51() Unknown
kernel32.dll!75b58543() Unknown
ntdll.dll!77bbac69() Unknown
ntdll.dll!77bbac3c() Unknown
Here are a couple of the functoin
log_worker::~log_worker(void)
{
this->queue.interrupt();
service.join();
}
void log_worker::run(void)
{
while (true)
{
log_message* msg;
if (this->queue.dequeue(msg) == false)
break;
this->lg->print_log_message(msg);
delete msg;
}
}
bool dequeue(T& item)
{
std::unique_lock<std::mutex> lock(m);
// handles spurious wakeups
while (!this->data_available && !this->interrupted)
cv.wait(lock);
if (this->interrupted)
return false;
item = std::move(this->front());
this->pop();
if (this->empty())
this->data_available = false;
return true;
}
void interrupt(void)
{
std::unique_lock<std::mutex> lock(m);
this->interrupted = true;
cv.notify_all();
printf("notified threads...\n");
}
Looks like you have a detached thread SchedulerBase(could be any scheduled thread which still uses logger) which is still running while your application is stopped and logger is destroyed which caused the crash.
And log_worker is dynamically allocated in the logger class.
You need to make sure all threads who use logger instance are shutdown properly before logger is destroyed
delete log_worker in logger destructor
I simply had to stop deleting things on shutdown. When you close the console using the 'X' button, it's not a proper shutdown, so It's pointless trying to shutdown threads.
I have a function called PreProcessSource, which allocates a boost::wave::context and does some preprocessing; nothing fancy at all.
std::string PreProcessSource(const std::string& instring, const std::string& defines)
{
typedef boost::wave::cpplexer::lex_token<> token_type;
typedef boost::wave::cpplexer::lex_iterator<token_type> lex_iterator_type;
typedef boost::wave::context<std::string::iterator, lex_iterator_type> context_type;
std::string source = instring;
context_type ctx(source.begin(), source.end()); // DEADLOCK here
ctx.set_language(boost::wave::enable_emit_line_directives(ctx.get_language(), true));
if(!defines.empty())
{
std::vector<std::string> tokens;
Split<std::string>(defines, tokens, ",");
std::vector<std::string>::const_iterator cit = tokens.begin();
for (;cit != tokens.end(); ++cit)
ctx.add_macro_definition(*cit);
}
context_type::iterator_type first = ctx.begin();
context_type::iterator_type last = ctx.end();
std::string outstring;
while (first != last)
{
const token_type::string_type& value = (*first).get_value();
std::copy(value.begin(), value.end(), std::back_inserter(outstring));
++first;
}
return outstring;
}
In the past (this project is being modernized so it was broken for a long time) it used to work fine in the same setup:
Library A is a DLL that hosts the PreProcess source function, executable B uses the DLL A and can call PreProcess source, and DLL A can also sometimes call the function itself.
But right now this is no longer the case: whenever DLL A calls the function itself, or DLL A calls another function, which in turn calls back into DLL A PreProcess source, it deadlocks.
Here's an example that works just fine from my unit testing:
BOOST_AUTO_TEST_CASE(Preprocessor)
{
std::stringstream sstream;
sstream << "void main(inout float4 vtxInput : POSITION) { }" << std::endl << std::endl;
std::string source = sstream.str();
std::string defines = "";
try
{
std::string shaderCode = Nitro::PreProcessSource(source, defines);
} catch (boost::wave::preprocess_exception& pe)
{
std::cerr << pe.what() << std::endl;
}
}
And here is the weird bit, if DO_DEADLOCK is defined to 1 in the following piece of code, the deadlock will happen:
class Tutorial00 : public Game
{
public:
// ...
Tutorial00()
: Game()
{
#if !DO_DEADLOCK
std::stringstream sstream;
sstream << "void main(inout float4 vtxInput : POSITION) { }" << std::endl << std::endl;
std::string source = sstream.str();
std::string defines = "";
std::string shaderCode = Nitro::PreProcessSource(source, defines);
#endif
}
void LoadContent()
{
#if DO_DEADLOCK
std::stringstream sstream;
sstream << "void main(inout float4 vtxInput : POSITION) { }" << std::endl << std::endl;
std::string source = sstream.str();
std::string defines = "";
std::string shaderCode = Nitro::PreProcessSource(source, defines);
#endif
// Original code that deadlocks too.
// Calls in the DLL, which will call PreProcessSource.
// effect->Initialize(device, source, "DX11");
}
// ...
};
#if 1
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
#else
int main(int argc, char* argv[])
#endif
{
Tutorial00 app;
app.Run();
return 0;
}
Note that the constructor is called directly from the executable, whereas LoadContent is called from the DLL:
// In the DLL
void Game::Run()
{
try
{
// ...
LoadContent();
// ...
} catch(/* ... */) { }
}
The code is compiled for x64 and in debug mode.
I compile boost myself into a DLL (using bcp to get the files for the libraries I use) with the following options:
Preprocessor:
WIN32
BOOST_ALL_NO_LIB
BOOST_ALL_DYN_LINK
BOOST_THREAD_BUILD_DLL
_DLL
_DEBUG
_WINDOWS
_USRDLL
Code Generation:
C++ Exceptions (/EHsc)
Multi-threaded Debug DLL (/MDd)
Function-level linking (/Gy)
Streaming SIMD Extensions 2 (/arch:SSE2) (/arch:SSE2)
Fast floating point model (/fp:fast)
DLL A uses the same options except for BOOST_ALL_DYN_LINK, BOOST_THREAD_BUILD_DLL and the string pooling option.
The Boost unit test library is build separately as a static library.
Here is the stack trace of the working version:
Nitro.dll!boost::call_once<void (__cdecl*)(void)>(boost::once_flag & flag, void (void)* f) Line 200 C++
Nitro.dll!boost::call_once(void (void)* func, boost::once_flag & flag) Line 28 C++
Nitro.dll!boost::spirit::classic::static_<boost::thread_specific_ptr<boost::weak_ptr<boost::spirit::classic::impl::grammar_helper ... Line 72 + 0x13 bytes C++
Nitro.dll!boost::spirit::classic::impl::get_definition<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 241 + 0x17 bytes C++
Nitro.dll!boost::spirit::classic::impl::grammar_parser_parse<0,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 296 + 0xa bytes C++
Nitro.dll!boost::spirit::classic::grammar<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 55 + 0x3c bytes C++
Nitro.dll!boost::spirit::classic::grammar<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 65 + 0x75 bytes C++
Nitro.dll!boost::spirit::classic::impl::phrase_parser<boost::spirit::classic::space_parser>::parse<char const * __ptr64,boost::wave::util::time_conversion::time_conversion_grammar> ... Line 136 C++
Nitro.dll!boost::spirit::classic::parse<char const * __ptr64,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 155 + 0x3a bytes C++
Nitro.dll!boost::spirit::classic::parse<char,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 173 + 0x23 bytes C++
Nitro.dll!boost::wave::util::time_conversion::time_conversion_helper::time_conversion_helper(const char * act_time) Line 123 C++
Nitro.dll!boost::wave::util::predefined_macros::predefined_macros() Line 196 + 0x38 bytes C++
...
Nitro.dll!Nitro::PreProcessSource(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & instring, const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & defines) Line 51 + 0xa0 bytes C++
NitroCoreUnitTests.exe!Preprocessor::test_method() Line 16 C++
NitroCoreUnitTests.exe!Preprocessor_invoker() Line 7 + 0x1f bytes C++
...
NitroCoreUnitTests.exe!boost::unit_test::unit_test_main(boost::unit_test::test_suite * (int, char * *)* init_func, int argc, char * * argv) Line 187 C++
NitroCoreUnitTests.exe!main(int argc, char * * argv) Line 238 C++
NitroCoreUnitTests.exe!__tmainCRTStartup() Line 555 + 0x19 bytes C
NitroCoreUnitTests.exe!mainCRTStartup() Line 371 C
kernel32.dll!00000000766a652d()
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]
ntdll.dll!0000000076d9c521()
And here is the stack trace of the deadlock:
ntdll.dll!0000000076dc135a()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
KernelBase.dll!000007fefd4f10dc()
Nitro.dll!boost::call_once<void (__cdecl*)(void)>(boost::once_flag & flag, void (void)* f) Line 197 + 0x18 bytes C++
Nitro.dll!boost::call_once(void (void)* func, boost::once_flag & flag) Line 28 C++
Nitro.dll!boost::spirit::classic::static_<boost::thread_specific_ptr<boost::weak_ptr<boost::spirit::classic::impl::grammar_helper ... Line 72 + 0x13 bytes C++
Nitro.dll!boost::spirit::classic::impl::get_definition<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 241 + 0x17 bytes C++
Nitro.dll!boost::spirit::classic::impl::grammar_parser_parse<0,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 296 + 0xa bytes C++
Nitro.dll!boost::spirit::classic::grammar<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 55 + 0x3c bytes C++
Nitro.dll!boost::spirit::classic::grammar<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 65 + 0x75 bytes C++
Nitro.dll!boost::spirit::classic::impl::phrase_parser<boost::spirit::classic::space_parser>::parse<char const * __ptr64,boost::wave::util::time_conversion::time_conversion_grammar> ... Line 136 C++
Nitro.dll!boost::spirit::classic::parse<char const * __ptr64,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 155 + 0x3a bytes C++
Nitro.dll!boost::spirit::classic::parse<char,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 173 + 0x23 bytes C++
Nitro.dll!boost::wave::util::time_conversion::time_conversion_helper::time_conversion_helper(const char * act_time) Line 123 C++
Nitro.dll!boost::wave::util::predefined_macros::predefined_macros() Line 196 + 0x38 bytes C++
...
Nitro.dll!Nitro::PreProcessSource(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & instring, const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & defines) Line 51 + 0xa0 bytes C++
Tutorial01.exe!Tutorial01::LoadContent() Line 70 + 0x33 bytes C++
Nitro.dll!Nitro::Game::Run() Line 40 C++
Tutorial01.exe!wWinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, wchar_t * lpCmdLine, int nCmdShow) Line 149 C++
Tutorial01.exe!__tmainCRTStartup() Line 547 + 0x42 bytes C
Tutorial01.exe!wWinMainCRTStartup() Line 371 C
kernel32.dll!00000000766a652d()
ntdll.dll!0000000076d9c521()
It seems boost::wave is trying to parse some time stamp, and by doing so instantiates a grammar and that's when things seem to go South.
Thanks in advance for any help :)
I found the following ticket opened on boost's trac: boost::call_once not re-entrant (at least in win32)
It says that call_once is not re-entrant, and shouldn't be called recursively. Thus the code that I am employing is undefined behavior; the ticket was marked as "will not fix".
The solution I've taken was to create a separate DLL that only contains the PreProcessSource function.