Unhandled exception at the end of thread - c++

Hello right now i'm trying to use a thread to read some info about 2 images using OpenCV 2.3.1, while I can retrieve the sizes of the 2 images without a problem, an error occurs when I reach the end of the thread.
Unhandled exception at 0x00348A56 in OpenCvDemo.exe: An invalid parameter was passed to a function that considers invalid parameters fatal.
Since I can read the size of the images just fine I know they aren't empty or anything. In another test I detached the thread and put it to Sleep for 5 seconds, the main function executed fine and displayed the image, but once the thread woke up and reached the end it crashed the application and displayed the same error as above.
Sadly I need to use OpenCV 2.3.1 since this is just a test for a bigger application.
How can I solve this problem, or at least what is the cause of it?
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <thread>
#include <opencv2/opencv.hpp>
void Loop(cv::Mat &image1, cv::Mat &image2) {
std::vector<uchar> buff1;
std::vector<uchar> buff2;
cv::imencode(".png", image1, buff1);
cv::imencode(".png", image2, buff2);
int imageSize1 = buff1.size();
int imageSize2 = buff2.size();
std::cout << "Size " << imageSize1 << "\n";
std::cout << "Size " << imageSize2 << "\n";
}
int main()
{
cv::Mat image1 = cv::imread("C:\\Users\\Cesar\\Pictures\\happyface.png", 1);
cv::Mat image2 = cv::imread("C:\\Users\\Cesar\\Pictures\\happyface.png", 1);
std::thread mythread(Loop, std::ref(image1), std::ref(image2));
mythread.join();
cv::imshow("name", image1);
cv::waitKey(0);
return 0;
}
The callstack
OpenCvDemo.exe!_invoke_watson(const wchar_t * expression, const wchar_t * function_name, const wchar_t * file_name, unsigned int line_number, unsigned int reserved) Line 224 C++
OpenCvDemo.exe!_invalid_parameter(const wchar_t * expression, const wchar_t * function_name, const wchar_t * file_name, unsigned int line_number, unsigned int reserved) Line 113 C++
[External Code]
OpenCvDemo.exe!Loop(cv::Mat & image1, cv::Mat & image2) Line 23 C++
[External Code]
OpenCvDemo.exe!invoke_thread_procedure(unsigned int(__stdcall*)(void *) procedure, void * const context) Line 92 C++
OpenCvDemo.exe!thread_start<unsigned int (__stdcall*)(void *)>(void * const parameter) Line 115 C++
[External Code]
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]

Related

Too few arguments to function 'int fclose(FILE*)'

Hello I am a beginner of C language for microprocessors. I want to read a ''.bmp'' file in order to apply line detection on it. I have declared a function to read the image. This error occurs when compile button is pushed:
#include "esp_camera.h"
#include "Arduino.h"
#include "FS.h" // SD Card ESP32
#include "SD_MMC.h" // SD Card ESP32
#include "soc/soc.h" // Disable brownour problems
#include "soc/rtc_cntl_reg.h" // Disable brownour problems
#include "driver/rtc_io.h"
#include <EEPROM.h> // read and write from flash memory
#include <SPI.h>
void imageReader(const char *imgName,
int *height,
int *width,
int *bitdepth,
unsigned char *header,
unsigned char *_colortable,
unsigned char *buf
) // READ AN IMAGE
{
int i;
fs::FS &fs = SD_MMC; //
FILE *file;
file = fopen(imgName,"rb"); // read imgName file ( it is a picture in .bmp format )
if(!file){
Serial.printf("Unable to read image");
}
for(i=0 ; i<54 ; i++){
header[i]=getc(file);
}
*width = *(int * )& header[18]; // width information of the image
*height = *(int * )& header[22]; // height information of image
*bitdepth = *(int *)& header[28];
if(*bitdepth<=8){
fread(_colortable,sizeof(unsigned char),1024,file);
}
fread(buf,sizeof(unsigned char),( 1600 * 1200 ) ,file);
fclose();
}
It gives this error. too few arguments to function 'int fclose(FILE*)'
The fclose() function needs to know which file to close. You need to tell it that by supplying "file" as an argument. You want to use fclose(file).

How can I maintain the call stack when I throw an exception from a thread in C++?

UPDATE 1
We run some large threads (threads with lots of code). We're not getting a reliable call stack that points us to the exception being thrown.
To make this example more reproducible and simpler, I boiled down the code to the following. I also removed the requirements of using /EHa and VS2013. This code is being compiled with VS2015 (v140) and default exception handling. I'm trying to figure out why the call stack doesn't ever seem to include the line that the std::runtime_error is thrown from.
#include <iostream>
#include <thread>
void myLongRunningMethod()
{
// Imagine lots of code here. We need to know where the exception
// is being thrown. Just knowing that it was thrown from this thread
// isn't sufficient.
std::cout << "..throwing from myLongRunningMethod()" << std::endl;
throw std::runtime_error("Test throw of std::runtime_error from thread"); // We would like to see this line in the call stack. We don't :(.
}
void main()
{
std::cout << "in main()" << std::endl;
// Spin up a thread and have it throw
std::cout << ".spinning up a thread" << std::endl;
std::thread t1(&myLongRunningMethod);
// Wait for thread to complete
std::cout << "...joining" << std::endl;
t1.join();
std::cout << "....join complete" << std::endl;
}
Output:
in main()
.spinning up a thread
..throwing from myLongRunningMethod()
...joining
Then I am presented with the abort() dialog. I click 'Retry'. Here is the callstack I get:
ucrtbased.dll!issue_debug_notification(const wchar_t * const message) Line 125 C++
ucrtbased.dll!__acrt_report_runtime_error(const wchar_t * message) Line 142 C++
ucrtbased.dll!abort() Line 51 C++
ucrtbased.dll!terminate() Line 59 C++
vcruntime140d.dll!FindHandler(EHExceptionRecord * pExcept, EHRegistrationNode * pRN, _CONTEXT * pContext, void * pDC, const _s_FuncInfo * pFuncInfo, unsigned char recursive, int CatchDepth, EHRegistrationNode * pMarkerRN) Line 714 C++
vcruntime140d.dll!__InternalCxxFrameHandler(EHExceptionRecord * pExcept, EHRegistrationNode * pRN, _CONTEXT * pContext, void * pDC, const _s_FuncInfo * pFuncInfo, int CatchDepth, EHRegistrationNode * pMarkerRN, unsigned char recursive) Line 439 C++
vcruntime140d.dll!__CxxFrameHandler(EHExceptionRecord * pExcept, EHRegistrationNode * pRN, void * pContext, void * pDC) Line 233 C++
ntdll.dll!ExecuteHandler2#20() Unknown
ntdll.dll!ExecuteHandler#20() Unknown
EhTestProject.exe!std::_Invoker_functor::_Call<void (__cdecl*)(void)>(void(*)() && _Obj) Line 1375 C++
EhTestProject.exe!std::_Pad::_Call_func(void * _Data) Line 209 C++
ucrtbased.dll!invoke_thread_procedure(unsigned int(__stdcall*)(void *) procedure, void * const context) Line 92 C++
ucrtbased.dll!thread_start<unsigned int (__stdcall*)(void *)>(void * const parameter) Line 115 C++
kernel32.dll!#BaseThreadInitThunk#12() Unknown
ntdll.dll!___RtlUserThreadStart#8() Unknown
ntdll.dll!__RtlUserThreadStart#8() Unknown
FURTHER UNDERSTANDING
I figured out why I was getting two dialog boxes (the main thread and the worker thread were each producing one). This was solved by adding a .join call to the main thread. Also, I do expect the thread to terminate on an unhandled exception (which explains why two of the three catch... blocks weren't getting entered).
ORIGINAL POST
Here's some code that isn't giving me the call stack I would expect and I'm unsure why. I've tried various combinations of try/catch without getting a full call stack.
I can't use the solution where std::current_exception is stored off
in a global because the threads in question are long running and we
don't join them to our main process.
We must use Async exception handling (/EHa), so a catch(...) block
will catch structured exceptions as well as standard exceptions, thus
treating all exceptions caught in a catch(...) block as fatal.
I can only use feautres supported by the v120 (2013) Microsoft C++ compiler.
Ideally, I want a full call stack to debug with. I'm having trouble figuring out why/how call stacks are being sliced.
// ------------------------------------------------------------
// Main.cpp
#include "OtherDll.h"
#include <iostream>
#include <memory>
#include <windows.h>
LPTOP_LEVEL_EXCEPTION_FILTER topLevelEF = nullptr;
static bool exceptionIsHandled = false;
LONG WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS p)
{
std::cout << "....in UnhandledExceptionFilter (exceptionIsHandled = " << exceptionIsHandled << ")" << std::endl;
// Handle exception (in case the top-level try catch missed it)
return EXCEPTION_CONTINUE_SEARCH;
}
void main()
{
// Microsoft best practice is to call SetErrorMode()
::SetErrorMode(SEM_FAILCRITICALERRORS);
::SetThreadErrorMode(SEM_FAILCRITICALERRORS, nullptr);
// Setup the process global unhandled exception filter to be called for an unhandled exception occurs
topLevelEF = ::SetUnhandledExceptionFilter(UnhandledExceptionFilter);
std::cout << "in main()" << std::endl;
auto otherDll = std::make_unique<OtherDll>();
try
{
otherDll->throwStdRuntimeExceptionFromThread();
}
catch (...)
{
std::cout << "..caught(...) in main(). Rethrowing with throw." << std::endl;
exceptionIsHandled = true;
throw;
}
system("pause"); // Windows specific
}
// ------------------------------------------------------------
// OtherDll.h
#pragma once
#ifndef OTHERDLL_API
#define OTHERDLL_API
#endif
class OtherDll
{
public:
OTHERDLL_API OtherDll();
static OTHERDLL_API void throwStdRuntimeExceptionFromThread();
private:
static void myPrivateThrowMethod();
};
// ------------------------------------------------------------
// OtherDll.cpp
#define OTHERDLL_API __declspec(dllexport)
#include "OtherDll.h"
#include <thread>
#include <iostream>
OtherDll::OtherDll()
{
std::cout << "..in OtherDll CTOR" << std::endl;
}
void OtherDll::throwStdRuntimeExceptionFromThread()
{
try
{
std::cout << "....calling thread to throw from try" << std::endl;
std::thread t1(&OtherDll::myPrivateThrowMethod);
}
catch (...)
{
std::cout << "......in catch(...)" << std::endl;
// handle exception
throw; //crash the process
}
}
void OtherDll::myPrivateThrowMethod()
{
try
{
std::cout << "......throwing from thread try" << std::endl;
throw std::runtime_error("Test from thread");
}
catch (...)
{
std::cout << "......caught by thread catch(...). Rethrowing." << std::endl;
throw; // must always rethrow because catching SEH (like access violations that are unrecoverable)
}
}
Output:
in main()
..in OtherDll CTOR
....calling thread to throw from try
......throwing from thread try
......caught by thread catch(...). Rethrowing.
At this point a 'Microsoft Visual C++ Runtime Library' dialog pops up telling me that abort() has been called. I click Retry to debug the application. I then get the next line of output from my program:
....in UnhandledExceptionFilter (exceptionIsHandled = 0)
To add to my confusion, sometimes I get two of the aforementioned VisualC++ dialog boxes. Also, sometimes (very rarely)...it does give me the call stack I would expect. Here are the two call stacks I'll get:
99% of the time (not helpful):
msvcr120d.dll!_NMSG_WRITE(int rterrnum) Line 226 C
msvcr120d.dll!abort() Line 62 C
msvcr120d.dll!terminate() Line 97 C++
msvcp120d.dll!_Call_func(void * _Data) Line 39 C++
msvcr120d.dll!_callthreadstartex() Line 376 C
msvcr120d.dll!_threadstartex(void * ptd) Line 359 C
kernel32.dll!#BaseThreadInitThunk#12() Unknown
ntdll.dll!___RtlUserThreadStart#8() Unknown
ntdll.dll!__RtlUserThreadStart#8() Unknown
1% of the time (good stack):
MSVCR120D.dll!_NMSG_WRITE(int rterrnum) Line 226 C
MSVCR120D.dll!abort() Line 62 C
MSVCR120D.dll!terminate() Line 97 C++
OtherDll.dll!std::thread::~thread() Line 56 C++
OtherDll.dll!OtherDll::throwStdRuntimeExceptionFromThread() Line 47 C++
EhTestProject.exe!main() Line 47 C++

Segmentation fault with avcodec_encode_video2() while encoding H.264

I'm trying to convert a cv::Mat to an AVFrame to encode it then in H.264 and wanted to start from a simple example, as I'm a newbie in both. So I first read in a JPEG file, and then do the pixel format conversion with sws_scale() from AV_PIX_FMT_BGR24 to AV_PIX_FMT_YUV420P keeping the dimensions the same, and it all goes fine until I call avcodec_encode_video2().
I read quite a few discussions regarding an AVFrame allocation and the question Segmentation fault while avcodec_encode_video2 seemed like a match but I just can't see what I'm missing or getting wrong.
Here is the minimal code that you can reproduce the crash, it should be compiled with,
g++ -o OpenCV2FFmpeg OpenCV2FFmpeg.cpp -lopencv_imgproc -lopencv_highgui -lopencv_core -lswscale -lavutil -lavcodec -lavformat
It's output on my system,
cv::Mat [width=420, height=315, depth=0, channels=3, step=1260]
I'll soon crash..
Segmentation fault
And that sample.jpg file's details by identify tool,
~temporary/sample.jpg JPEG 420x315 420x315+0+0 8-bit sRGB 38.3KB 0.000u 0:00.000
Please note that I'm trying to create a video out of a single image, just to keep things simple.
#include <iostream>
#include <cassert>
using namespace std;
extern "C" {
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
}
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
const string TEST_IMAGE = "/home/baris/temporary/sample.jpg";
int main(int /*argc*/, char** argv)
{
av_register_all();
avcodec_register_all();
/**
* Initialise the encoder
*/
AVCodec *h264encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
AVFormatContext *cv2avFormatContext = avformat_alloc_context();
/**
* Create a stream and allocate frames
*/
AVStream *h264outputstream = avformat_new_stream(cv2avFormatContext, h264encoder);
avcodec_get_context_defaults3(h264outputstream->codec, h264encoder);
AVFrame *sourceAvFrame = av_frame_alloc(), *destAvFrame = av_frame_alloc();
int got_frame;
/**
* Pixel formats for the input and the output
*/
AVPixelFormat sourcePixelFormat = AV_PIX_FMT_BGR24;
AVPixelFormat destPixelFormat = AV_PIX_FMT_YUV420P;
/**
* Create cv::Mat
*/
cv::Mat cvFrame = cv::imread(TEST_IMAGE, CV_LOAD_IMAGE_COLOR);
int width = cvFrame.size().width, height = cvFrame.size().height;
cerr << "cv::Mat [width=" << width << ", height=" << height << ", depth=" << cvFrame.depth() << ", channels=" << cvFrame.channels() << ", step=" << cvFrame.step << "]" << endl;
h264outputstream->codec->pix_fmt = destPixelFormat;
h264outputstream->codec->width = cvFrame.cols;
h264outputstream->codec->height = cvFrame.rows;
/**
* Prepare the conversion context
*/
SwsContext *bgr2yuvcontext = sws_getContext(width, height,
sourcePixelFormat,
h264outputstream->codec->width, h264outputstream->codec->height,
h264outputstream->codec->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
/**
* Convert and encode frames
*/
for (uint i=0; i < 250; i++)
{
/**
* Allocate source frame, i.e. input to sws_scale()
*/
avpicture_alloc((AVPicture*)sourceAvFrame, sourcePixelFormat, width, height);
for (int h = 0; h < height; h++)
memcpy(&(sourceAvFrame->data[0][h*sourceAvFrame->linesize[0]]), &(cvFrame.data[h*cvFrame.step]), width*3);
/**
* Allocate destination frame, i.e. output from sws_scale()
*/
avpicture_alloc((AVPicture *)destAvFrame, destPixelFormat, width, height);
sws_scale(bgr2yuvcontext, sourceAvFrame->data, sourceAvFrame->linesize,
0, height, destAvFrame->data, destAvFrame->linesize);
/**
* Prepare an AVPacket for encoded output
*/
AVPacket avEncodedPacket;
av_init_packet(&avEncodedPacket);
avEncodedPacket.data = NULL;
avEncodedPacket.size = 0;
// av_free_packet(&avEncodedPacket); w/ or w/o result doesn't change
cerr << "I'll soon crash.." << endl;
if (avcodec_encode_video2(h264outputstream->codec, &avEncodedPacket, destAvFrame, &got_frame) < 0)
exit(1);
cerr << "Checking if we have a frame" << endl;
if (got_frame)
av_write_frame(cv2avFormatContext, &avEncodedPacket);
av_free_packet(&avEncodedPacket);
av_frame_free(&sourceAvFrame);
av_frame_free(&destAvFrame);
}
}
Thanks in advance!
EDIT: And the stack trace after the crash,
Thread 2 (Thread 0x7fffe5506700 (LWP 10005)):
#0 0x00007ffff4bf6c5d in poll () at /lib64/libc.so.6
#1 0x00007fffe9073268 in () at /usr/lib64/libusb-1.0.so.0
#2 0x00007ffff47010a4 in start_thread () at /lib64/libpthread.so.0
#3 0x00007ffff4bff08d in clone () at /lib64/libc.so.6
Thread 1 (Thread 0x7ffff7f869c0 (LWP 10001)):
#0 0x00007ffff5ecc7dc in avcodec_encode_video2 () at /usr/lib64/libavcodec.so.56
#1 0x00000000004019b6 in main(int, char**) (argv=0x7fffffffd3d8) at ../src/OpenCV2FFmpeg.cpp:99
EDIT2: Problem was that I hadn't avcodec_open2() the codec as spotted by Ronald. Final version of the code is at https://github.com/barisdemiray/opencv2ffmpeg/, with leaks and probably other problems hoping that I'll improve it while learning both libraries.
Please provide a gdb backtrace after it crashes. I'm pretty sure it'll crash because your picture pointers are unaligned (avpicture_alloc() will allocate unaligned images, and avcodec_encode_video2() expects aligned data pointers), but there may be additional reasons why it would crash also.
To align data pointers in a AVFrame, use av_image_alloc() and related functions, with align=32.
Another problem is that you didn't call avcodec_open2(h264outputstream->codec, h264encoder).

Error in itk::BinaryImageToLabelMapFilter for images with 1 row and N columns

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

Why does boost::wave::context constructor deadlocks when called from a DLL?

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.