3

我有一个名为 PreProcessSource 的函数,它分配一个 boost::wave::context 并进行一些预处理;没有什么花哨的。

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;
}

在过去(这个项目正在现代化,所以它被破坏了很长时间)它曾经在相同的设置中工作正常:

库 A 是承载 PreProcess 源函数的 DLL,可执行文件 B 使用 DLL A 并可以调用 PreProcess 源,DLL A 有时也可以调用函数本身。

但现在情况不再如此:每当 DLL A 调用函数本身,或 DLL A 调用另一个函数,而后者又调用回 DLL A PreProcess 源时,它就会死锁。

这是一个在我的单元测试中运行良好的示例:

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;
    }
}

奇怪的是,如果在以下代码中将 DO_DEADLOCK 定义为 1,就会发生死锁:

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;
}

请注意,构造函数是直接从可执行文件调用的,而 LoadContent 是从 DLL 调用的:

// In the DLL
void Game::Run()
{
    try
    {
        // ...

        LoadContent();

        // ...
    } catch(/* ... */) { }
}

该代码是为 x64 和调试模式编译的。我使用以下选项将 boost 自己编译成一个 DLL(使用 bcp 来获取我使用的库的文件):

预处理器:

  • WIN32
  • BOOST_ALL_NO_LIB
  • BOOST_ALL_DYN_LINK
  • BOOST_THREAD_BUILD_DLL
  • _DLL
  • _调试
  • _视窗
  • _USRDLL

代码生成:

  • C++ 异常 (/EHsc)
  • 多线程调试 DLL (/MDd)
  • 功能级链接 (/Gy)
  • 流式 SIMD 扩展 2 (/arch:SSE2) (/arch:SSE2)
  • 快速浮点模型 (/fp:fast)

除了 BOOST_ALL_DYN_LINK、BOOST_THREAD_BUILD_DLL 和字符串池选项之外,DLL A 使用相同的选项。

Boost 单元测试库作为静态库单独构建。

这是工作版本的堆栈跟踪:

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()    

这是死锁的堆栈跟踪:

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()    

似乎 boost::wave 正在尝试解析一些时间戳,并通过这样做实例化一个语法,这就是事情似乎向南的时候。

在此先感谢您的帮助:)

4

1 回答 1

1

我发现在 boost 的 trac 上打开了以下票:boost::call_once not re-entrant (至少在 win32 中)

它说 call_once 不是可重入的,不应该被递归调用。因此,我使用的代码是未定义的行为;该票被标记为“将无法修复”。

我采取的解决方案是创建一个单独的 DLL,它只包含 PreProcessSource 函数。

于 2012-06-23T12:23:13.997 回答