> I've been looking at your signal handling implementation in >execution_monitor.cpp, and I think I've uncovered quite a few bugs, some of >which are really quite fatal for multithreading code.
The code never promised to work in multithreaded environment, nor even to be thread save. It is in my to-do list. Though recent hands in several situations may require address some of these issues sooner. >Issue 1: >~~~~~ >You use a singleton for the signal handling jump: > >inline sigjmp_buf & execution_monitor_jump_buffer() >{ > static sigjmp_buf unit_test_jump_buffer_; > return unit_test_jump_buffer_; >} > >There are two issues with this: > >a) It is not reentrant: if the monitored procedure called by catch_signals >calls another monitored procedure then the inner catch_signals call will >overwrite the jump data for the outer call. IMO this situation is quite >common - and actually occurs in your own unit test code I believe. No. I don't think it's common situation. You don't usually create and run test cases inside the other test case code. I also have special precautions to make sure it does not happened with test suites. As for my own unit tests I will recheck whether not this is the issue. >b) It is not thread safe: each thread will trash the main threads jump >data - when you signal handler gets called it will jump onto some other >threads stack - or even the stack of a terminated thread! > >To fix this you will need to create the sigjmp_buf data on your program >stack (in catch_signals), then store a pointer to it in a global variable, >you will also need to back up that pointer on entering catch_signals and >restore on leaving: smells like a scoped object to me: > >class sigjmp_data >{ >sigjmp_buf m_buf; >sigjmp_buf* m_pold; >static sigjmp_buf* s_pnext; > >public: >sigjmp_data() >{ m_pold = s_pnext; s_pnext = &m_buf; } > >~sigjmp_data() >{ s_pnext = m_pold; } > >static sigjmp_data& get() >{ return *s_pnext; } > >}; Sound very reasonable. >To make this thread safe you would need to store the pointer in a thread >local storage slot, BTW I don't think you can use boost.threads for this, as >it will create a dependency quagmire for status/Jamfile :-( I thought to introduce macro BOOST_MULTITHREADED_UNIT_TEST and guard all usage of Boost.Thread with it. It does not create extra dependency and should allow to build multithreaded version with bjam subvariant feature. >Issue 2: >~~~~~ > >In catch_signals you have: > > static struct sigaction all_signals_action; > >except this creates a race condition during this structs initialisation: >getting rid of the static declaritor should do the trick. I don't even remember why it's static in a first place. > >Issue 3: >~~~~~ > >Your invocation of sigsetjmp is strictly speaking invoking undefined >behavior: > > volatile int sigtype = sigsetjmp( execution_monitor_jump_buffer(), 1 ); > > here's what POSIX issue 6 says: > >"An application shall ensure that an invocation of setjmp( ) appears in one >of the following >40418 contexts only: >40419 • The entire controlling expression of a selection or iteration >statement >40420 • One operand of a relational or equality operator with the other >operand an integral constant >40421 expression, with the resulting expression being the entire controlling >expression of a >40422 selection or iteration statement >40423 • The operand of a unary ’!’ operator with the resulting expression >being the entire >40424 controlling expression of a selection or iteration >40425 • The entire expression of an expression statement (possibly cast to >void) >40426 If the invocation appears in any other context, the behavior is >undefined." > >Note that although this refers to setjmp, sigsetjmp makes reference to it. I will need to investigate this in more details. The only thing that I could comment right away is that it seems to work in majority of the cases. >Issue 4: >~~~~~ > >Your calls to sigaction are not exception safe: if your monitored function >throws an exception (not an unlikely event), then the original signal >contexts are not restored, using more scoped objects should fix this. Yep. That's right. >Issue 5: >~~~~~ > >Your report error_code is not thread safe: > >static void report_error( execution_exception::error_code ec, >c_string_literal msg1, c_string_literal msg2 ) >{ > static char buf[REPORT_ERROR_BUFFER_SIZE]; > buf[0] = '\0'; > std::strncat( buf, msg1, sizeof(buf)-1 ); > std::strncat( buf, msg2, sizeof(buf)-1-std::strlen(buf) ); > throw execution_exception( ec, buf ); >} > >removing the static declarator from the buffer should fix that. No. It would lead to error, cause execution_exception expect C string not std::string and accordingly will point to dead memory. What I need here is thread specific storage. It still won't be reentrant, but it won't be the issue IMO. >The difficulty we have now is that there is a release coming up, and >boost.test is pretty mission critical for that, and these aren't really >trivial issues to fix, I'm not sure how we should handle that - ideas? I was aware of thread safety issues. And still I don't think it is so critical, that we need to hurry to fix it for this release. My plan was to address it after CLA. I still hope to be able to use Boost.Thread for this. I will try to address 1(without tss) 2 and 4 today. >Hope this helps, Thank you John for your time and efforts. We should have found this during review. Gennadiy. _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost