From 44b80414d3ec94cf5d7438a8a51bcb72ac8379b1 Mon Sep 17 00:00:00 2001 From: kgv Date: Mon, 17 Jun 2019 18:42:36 +0300 Subject: [PATCH] 0030775: Foundation Classes - Preserve application-defined top-level exception filter New overload for method OSD::SetSignal() is added accepting argument specifying how to set or unset handlers. New enum OSD_SignalMode describes different modes of signal handlers setting used in extended version of OSD::SetSignal(). Method OSD::SignalMode() returns mode set by the last call to SetSignal(). Method OSD::IsFloatingSignalSet() is changed to return value based on actual floating point exception flags (rather than on variable set by previous call to SetSignal()). Added new method OSD::SetThreadLocalSignal() intended to setup thread-specific handlers (e.g. _set_se_translator() on Windows) and FPE settings. OSD_ThreadPool and Media_PlayerContext now use new method instead of OSD::SetSignal(), to avoid overriding global handlers. dsetsignal syntax has been extended to support choice of signal handling mode. DRAW command OCC30775 is added allowing to test signal handling in multithreaded process. --- src/Draw/Draw_BasicCommands.cxx | 73 ++++- src/Media/Media_PlayerContext.cxx | 6 +- src/OSD/FILES | 1 + src/OSD/OSD.hxx | 114 ++++---- src/OSD/OSD_SignalMode.hxx | 26 ++ src/OSD/OSD_ThreadPool.cxx | 4 +- src/OSD/OSD_signal.cxx | 425 +++++++++++++++--------------- src/QABugs/QABugs_11.cxx | 66 ++++- tests/bugs/fclasses/bug30775 | 24 ++ 9 files changed, 456 insertions(+), 283 deletions(-) create mode 100644 src/OSD/OSD_SignalMode.hxx create mode 100644 tests/bugs/fclasses/bug30775 diff --git a/src/Draw/Draw_BasicCommands.cxx b/src/Draw/Draw_BasicCommands.cxx index 8c57448a03..2ef68c331c 100644 --- a/src/Draw/Draw_BasicCommands.cxx +++ b/src/Draw/Draw_BasicCommands.cxx @@ -913,21 +913,70 @@ static int dperf (Draw_Interpretor& theDI, Standard_Integer theArgNb, const char static int dsetsignal (Draw_Interpretor& theDI, Standard_Integer theArgNb, const char** theArgVec) { - // arm FPE handler if argument is provided and its first symbol is not '0' - // or if environment variable CSF_FPE is set and its first symbol is not '0' - bool setFPE = false; - if (theArgNb > 1) + OSD_SignalMode aMode = OSD_SignalMode_Set; + Standard_Boolean aSetFPE = OSD::ToCatchFloatingSignals(); + + // default for FPE signal is defined by CSF_FPE variable, if set + OSD_Environment aEnv("CSF_FPE"); + TCollection_AsciiString aEnvStr = aEnv.Value(); + if (!aEnvStr.IsEmpty()) { - setFPE = (theArgVec[1][0] == '1' || theArgVec[1][0] == 't'); + aSetFPE = (aEnvStr.Value(1) != '0'); } - else + + // parse arguments + for (Standard_Integer anArgIter = 1; anArgIter < theArgNb; ++anArgIter) { - OSD_Environment aEnv ("CSF_FPE"); - TCollection_AsciiString aEnvStr = aEnv.Value(); - setFPE = (! aEnvStr.IsEmpty() && aEnvStr.Value(1) != '0'); + TCollection_AsciiString anArg(theArgVec[anArgIter]); + anArg.LowerCase(); + if (anArg == "asis") + { + aMode = OSD_SignalMode_AsIs; + } + else if (anArg == "set") + { + aMode = OSD_SignalMode_Set; + } + else if (anArg == "unhandled") + { + aMode = OSD_SignalMode_SetUnhandled; + } + else if (anArg == "unset") + { + aMode = OSD_SignalMode_Unset; + } + else if (anArg == "1" || anArg == "on") + { + aSetFPE = Standard_True; + } + else if (anArg == "0" || anArg == "off") + { + aSetFPE = Standard_False; + } + else if (anArg == "default") + { + } + else + { + std::cout << "Syntax error: unknown argument '" << anArg << "'\n"; + return 1; + } } - OSD::SetSignal (setFPE); - theDI << "Signal handlers are set, with FPE " << (setFPE ? "armed" : "disarmed"); + + OSD::SetSignal(aMode, aSetFPE); + + // report actual status in the end + const char* aModeStr = 0; + switch (OSD::SignalMode()) + { + default: + case OSD_SignalMode_AsIs: aModeStr = "asis"; break; + case OSD_SignalMode_Set: aModeStr = "set"; break; + case OSD_SignalMode_SetUnhandled: aModeStr = "unhandled"; break; + case OSD_SignalMode_Unset: aModeStr = "unset"; break; + } + theDI << "Signal mode: " << aModeStr << "\n" + << "Catch FPE: " << (OSD::ToCatchFloatingSignals() ? "1" : "0") << "\n"; return 0; } @@ -1057,7 +1106,7 @@ void Draw::BasicCommands(Draw_Interpretor& theCommands) __FILE__, dmeminfo, g); theCommands.Add("dperf","dperf [reset] -- show performance counters, reset if argument is provided", __FILE__,dperf,g); - theCommands.Add("dsetsignal","dsetsignal [fpe=0] -- set OSD signal handler, with FPE option if argument is given", + theCommands.Add("dsetsignal","dsetsignal [{asis|set|unhandled|unset}=set] [{0|1|default=$CSF_FPE}]\n -- set OSD signal handler, with FPE option if argument is given", __FILE__,dsetsignal,g); theCommands.Add("dparallel", diff --git a/src/Media/Media_PlayerContext.cxx b/src/Media/Media_PlayerContext.cxx index 537d293b20..a49b44bc6f 100644 --- a/src/Media/Media_PlayerContext.cxx +++ b/src/Media/Media_PlayerContext.cxx @@ -508,7 +508,11 @@ bool Media_PlayerContext::receiveFrame (const Handle(Media_Frame)& theFrame, //================================================================ void Media_PlayerContext::doThreadLoop() { - OSD::SetSignal (false); + // always set OCCT signal handler to catch signals if any; + // this is safe (for thread local handler) since the thread + // is owned by this class + OSD::SetThreadLocalSignal (OSD_SignalMode_Set, false); + Handle(Media_Frame) aFrame; bool wasSeeked = false; for (;;) diff --git a/src/OSD/FILES b/src/OSD/FILES index 488da0ccf9..d7615dbab5 100755 --- a/src/OSD/FILES +++ b/src/OSD/FILES @@ -93,3 +93,4 @@ OSD_Timer.hxx OSD_WhoAmI.hxx OSD_WNT.cxx OSD_WNT.hxx +OSD_SignalMode.hxx diff --git a/src/OSD/OSD.hxx b/src/OSD/OSD.hxx index dd80e028b0..03dc1eea87 100644 --- a/src/OSD/OSD.hxx +++ b/src/OSD/OSD.hxx @@ -16,49 +16,26 @@ #define _OSD_HeaderFile #include -#include -#include - -#include -#include -#include #include -#include -class OSD_Error; -class OSD_Protection; -class OSD_Path; -class OSD_FileNode; -class OSD_Disk; -class OSD_File; -class OSD_FileIterator; -class OSD_Directory; -class OSD_DirectoryIterator; -class OSD_Timer; -class OSD_Host; -class OSD_Environment; -class OSD_EnvironmentIterator; -class OSD_Process; -class OSD_SharedLibrary; -class OSD_Thread; +#include - -//! Set of Operating Sytem Dependent Tools -//! (O)perating (S)ystem (D)ependent +//! Set of Operating Sytem Dependent (OSD) Tools class OSD { public: DEFINE_STANDARD_ALLOC - - //! Sets signal and exception handlers. + //! Sets or removes signal and FPE (floating-point exception) handlers. + //! OCCT signal handlers translate signals raised by C subsystem to C++ + //! exceptions inheriting Standard_Failure. //! //! ### Windows-specific notes //! //! Compiled with MS VC++ sets 3 main handlers: //! @li Signal handlers (via ::signal() functions) that translate system signals //! (SIGSEGV, SIGFPE, SIGILL) into C++ exceptions (classes inheriting - //! Standard_Failure). They only be called if user calls ::raise() function + //! Standard_Failure). They only be called if function ::raise() is called //! with one of supported signal type set. //! @li Exception handler OSD::WntHandler() (via ::SetUnhandledExceptionFilter()) //! that will be used when user's code is compiled with /EHs option. @@ -71,41 +48,68 @@ public: //! compile his code with (/EHs or /EHa), signals (or SE exceptions) will be //! translated into Open CASCADE C++ exceptions. //! - //! If @a theFloatingSignal is TRUE then floating point exceptions will be - //! generated in accordance with the mask - //! _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW that is - //! used to call ::_controlfp() system function. If @a theFloatingSignal is FALSE - //! corresponding operations (e.g. division by zero) will gracefully complete - //! without an exception. + //! MinGW should use SEH exception mode for signal handling to work. //! - //! ### Unix-specific notes + //! ### Linux-specific notes //! //! OSD::SetSignal() sets handlers (via ::sigaction()) for multiple signals - //! (SIGFPE, SIGSEGV, etc). Currently the number of handled signals is much - //! greater than for Windows, in the future this may change to provide better - //! consistency with Windows. - //! - //! @a theFloatingSignal is recognized on Sun Solaris, Linux, and SGI Irix to - //! generate floating-point exception according to the mask - //! FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW (in Linux conventions).
- //! When compiled with OBJS macro defined, already set signal handlers (e.g. - //! by Data Base Managers) are not redefined. + //! (SIGFPE, SIGSEGV, etc). //! //! ### Common notes //! - //! If OSD::SetSignal() method is used in at least one thread, it must also be - //! called in any other thread where Open CASCADE will be used, to ensure - //! consistency of behavior. Its @a aFloatingSignal argument must be consistent - //! across threads. + //! If @a theFloatingSignal is TRUE then floating point exceptions will + //! generate SIGFPE in accordance with the mask + //! - Windows: _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW, + //! see _controlfp() system function. + //! - Linux: FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW, + //! see feenableexcept() system function. //! - //! Keep in mind that whether the C++ exception will really be thrown (i.e. - //! ::throw() will be called) is regulated by the - //! OCC_CONVERT_SIGNALS macro used during compilation of Open CASCADE and - //! user's code. Refer to Foundation Classes User's Guide for further details. + //! If @a theFloatingSignal is FALSE then floating point calculations will gracefully + //! complete regardless of occurred exceptions (e.g. division by zero). + //! Otherwise the (thread-specific) FPE flags are set to raise signal if one of + //! floating-point exceptions (division by zero, overflow, or invalid operation) occurs. //! - Standard_EXPORT static void SetSignal (const Standard_Boolean theFloatingSignal = Standard_True); + //! The recommended approach is to call OSD::SetSignal() in the beginning of the + //! execution of the program, in function main() or its equivalent. + //! In multithreaded programs it is advisable to call OSD::SetSignal() or + //! OSD::SetThreadLocalSignal() with the same parameters in other threads where + //! OCCT is used, to ensure consistency of behavior. + //! + //! Note that in order to handle signals as C++ exceptions on Linux and under + //! MinGW on Windows it is necessary to compile both OCCT and application with + //! OCC_CONVERT_SIGNALS macro, and use macro OCC_CATCH_SIGNALS within each try{} + //! block that has to catch this kind of exceptions. + //! + //! Refer to documentation of Standard_ErrorHandler.hxx for details. + Standard_EXPORT static void SetSignal (OSD_SignalMode theSignalMode, + Standard_Boolean theFloatingSignal); - //! Return floating signal catching value previously set by SetSignal(). + //! Sets signal and FPE handlers. + //! Short-cut for OSD::SetSignal (OSD_SignalMode_Set, theFloatingSignal). + static void SetSignal (const Standard_Boolean theFloatingSignal = Standard_True) + { + SetSignal (OSD_SignalMode_Set, theFloatingSignal); + } + + //! Initializes thread-local signal handlers. + //! This includes _set_se_translator() on Windows platform, and SetFloatingSignal(). + //! The main purpose of this method is initializing handlers for newly created threads + //! without overriding global handlers (set by application or by OSD::SetSignal()). + Standard_EXPORT static void SetThreadLocalSignal (OSD_SignalMode theSignalMode, + Standard_Boolean theFloatingSignal); + + //! Enables / disables generation of C signal on floating point exceptions (FPE). + //! This call does NOT register a handler for signal raised in case of FPE - + //! SetSignal() should be called beforehand for complete setup. + //! Note that FPE setting is thread-local, new threads inherit it from parent. + Standard_EXPORT static void SetFloatingSignal (Standard_Boolean theFloatingSignal); + + //! Returns signal mode set by the last call to SetSignal(). + //! By default, returns OSD_SignalMode_AsIs. + Standard_EXPORT static OSD_SignalMode SignalMode(); + + //! Returns true if floating point exceptions will raise C signal + //! according to current (platform-dependent) settings in this thread. Standard_EXPORT static Standard_Boolean ToCatchFloatingSignals(); //! Commands the process to sleep for a number of seconds. diff --git a/src/OSD/OSD_SignalMode.hxx b/src/OSD/OSD_SignalMode.hxx new file mode 100644 index 0000000000..b220b04a5b --- /dev/null +++ b/src/OSD/OSD_SignalMode.hxx @@ -0,0 +1,26 @@ +// Copyright (c) 2019 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef _OSD_SignalMode_HeaderFile +#define _OSD_SignalMode_HeaderFile + +//! Mode of operation for OSD::SetSignal() function +enum OSD_SignalMode +{ +OSD_SignalMode_AsIs, //!< Do not set or remove signal handlers +OSD_SignalMode_Set, //!< Set OCCT signal handlers +OSD_SignalMode_SetUnhandled, //!< Set OCCT signal handler but only if no handler is set, for each particular signal type +OSD_SignalMode_Unset //!< Unset signal handler to system default +}; + +#endif // _OSD_SignalMode_HeaderFile diff --git a/src/OSD/OSD_ThreadPool.cxx b/src/OSD/OSD_ThreadPool.cxx index 22c687703e..0d3bc2bb0e 100644 --- a/src/OSD/OSD_ThreadPool.cxx +++ b/src/OSD/OSD_ThreadPool.cxx @@ -309,7 +309,7 @@ void OSD_ThreadPool::performJob (Handle(Standard_Failure)& theFailure, // ======================================================================= void OSD_ThreadPool::EnumeratedThread::performThread() { - OSD::SetSignal (false); + OSD::SetThreadLocalSignal (OSD::SignalMode(), false); for (;;) { myWakeEvent.Wait(); @@ -322,7 +322,7 @@ void OSD_ThreadPool::EnumeratedThread::performThread() myFailure.Nullify(); if (myJob != NULL) { - OSD::SetSignal (myToCatchFpe); + OSD::SetThreadLocalSignal (OSD::SignalMode(), myToCatchFpe); OSD_ThreadPool::performJob (myFailure, myJob, myThreadIndex); myJob = NULL; } diff --git a/src/OSD/OSD_signal.cxx b/src/OSD/OSD_signal.cxx index 1b712deccf..654523afd9 100644 --- a/src/OSD/OSD_signal.cxx +++ b/src/OSD/OSD_signal.cxx @@ -16,16 +16,17 @@ #include #include #include +#include -static Standard_THREADLOCAL Standard_Boolean fFltExceptions = Standard_False; +static OSD_SignalMode OSD_WasSetSignal = OSD_SignalMode_AsIs; //======================================================================= -//function : ToCatchFloatingSignals +//function : SignalMode //purpose : //======================================================================= -Standard_Boolean OSD::ToCatchFloatingSignals() +OSD_SignalMode OSD::SignalMode() { - return fFltExceptions; + return OSD_WasSetSignal; } #ifdef _WIN32 @@ -90,7 +91,6 @@ static Standard_Boolean fDbgLoaded; static LONG _osd_debug ( void ); #endif -//# define _OSD_FPX ( _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW ) # define _OSD_FPX ( _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW ) #ifdef OCC_CONVERT_SIGNALS @@ -236,15 +236,16 @@ static LONG CallHandler (DWORD dwExceptionCode, } // end switch + // reset FPE state (before message box, otherwise it may fail to show up) + if ( flterr ) { + OSD::SetFloatingSignal (Standard_True); + } + +#if ! defined(OCCT_UWP) && !defined(__MINGW32__) && !defined(__CYGWIN32__) // provide message to the user with possibility to stop size_t idx; StringCchLengthW (buffer, _countof(buffer),&idx); if ( idx && fMsgBox && dwExceptionCode != EXCEPTION_NONCONTINUABLE_EXCEPTION ) { - // reset FP operations before message box, otherwise it may fail to show up - _fpreset(); - _clearfp(); - -#if ! defined(OCCT_UWP) && !defined(__MINGW32__) && !defined(__CYGWIN32__) MessageBeep ( MB_ICONHAND ); int aChoice = ::MessageBoxW (0, buffer, L"OCCT Exception Handler", MB_ABORTRETRYIGNORE | MB_ICONSTOP); if (aChoice == IDRETRY) @@ -253,17 +254,8 @@ static LONG CallHandler (DWORD dwExceptionCode, DebugBreak(); } else if (aChoice == IDABORT) exit(0xFFFF); + } #endif - } - - // reset FPE state - if ( flterr ) { - if ( !fFltExceptions ) return EXCEPTION_EXECUTE_HANDLER; - _fpreset () ; - _clearfp() ; - _controlfp ( 0, _OSD_FPX ) ; // JR add : -// std::cout << "OSD::WntHandler _controlfp( 0, _OSD_FPX ) " << std::hex << _controlfp(0,0) << std::dec << std::endl ; - } char aBufferA[2048]; WideCharToMultiByte(CP_UTF8, 0, buffer, -1, aBufferA, sizeof(aBufferA), NULL, NULL); @@ -367,13 +359,60 @@ static LONG WINAPI WntHandler (EXCEPTION_POINTERS *lpXP) lpXP->ExceptionRecord->ExceptionInformation[0]); } +//======================================================================= +//function : SetFloatingSignal +//purpose : +//======================================================================= +void OSD::SetFloatingSignal (Standard_Boolean theFloatingSignal) +{ + _fpreset(); + _clearfp(); + + // Note: zero bit means exception will be raised + _controlfp (theFloatingSignal ? 0 : _OSD_FPX, _OSD_FPX); +} + +//======================================================================= +//function : ToCatchFloatingSignals +//purpose : +//======================================================================= +Standard_Boolean OSD::ToCatchFloatingSignals() +{ + // return true if at least one of bits within _OSD_FPX + // is unset, which means relevant FPE will raise exception + int aControlWord = _controlfp (0, 0); + return (_OSD_FPX & ~aControlWord) != 0; +} + +//======================================================================= +//function : SetThreadLocalSignal +//purpose : +//======================================================================= +void OSD::SetThreadLocalSignal (OSD_SignalMode theSignalMode, + Standard_Boolean theFloatingSignal) +{ +#ifdef _MSC_VER + _se_translator_function aPreviousFunc = NULL; + if (theSignalMode == OSD_SignalMode_Set || theSignalMode == OSD_SignalMode_SetUnhandled) + aPreviousFunc = _set_se_translator(TranslateSE); + if (theSignalMode == OSD_SignalMode_Unset || (theSignalMode == OSD_SignalMode_SetUnhandled && aPreviousFunc != NULL)) + _set_se_translator(aPreviousFunc); +#else + (void)theSignalMode; +#endif + SetFloatingSignal (theFloatingSignal); +} + //======================================================================= //function : SetSignal //purpose : //======================================================================= -void OSD::SetSignal (const Standard_Boolean theFloatingSignal) +void OSD::SetSignal (OSD_SignalMode theSignalMode, + Standard_Boolean theFloatingSignal) { Standard_Mutex::Sentry aSentry (THE_SIGNAL_MUTEX); // lock the mutex to prevent simultaneous handling + OSD_WasSetSignal = theSignalMode; + #if !defined(OCCT_UWP) || defined(NTDDI_WIN10_TH2) OSD_Environment env ("CSF_DEBUG_MODE"); TCollection_AsciiString val = env.Value(); @@ -391,37 +430,53 @@ void OSD::SetSignal (const Standard_Boolean theFloatingSignal) // when user's code is compiled with /EHs // Replaces the existing top-level exception filter for all existing and all future threads // in the calling process - ::SetUnhandledExceptionFilter (/*(LPTOP_LEVEL_EXCEPTION_FILTER)*/ WntHandler); + { + LPTOP_LEVEL_EXCEPTION_FILTER aPreviousFunc = NULL; + if (theSignalMode == OSD_SignalMode_Set || theSignalMode == OSD_SignalMode_SetUnhandled) + { + aPreviousFunc = ::SetUnhandledExceptionFilter(WntHandler); + } + if (theSignalMode == OSD_SignalMode_Unset || (theSignalMode == OSD_SignalMode_SetUnhandled && aPreviousFunc != NULL)) + { + ::SetUnhandledExceptionFilter(aPreviousFunc); + } + } #endif // NTDDI_WIN10_TH2 - // Signal handlers will only be used when the method ::raise() will be used - // Handlers must be set for every thread - if (signal (SIGSEGV, (void(*)(int))SIGWntHandler) == SIG_ERR) - std::cout << "signal(OSD::SetSignal) error\n"; - if (signal (SIGFPE, (void(*)(int))SIGWntHandler) == SIG_ERR) - std::cout << "signal(OSD::SetSignal) error\n"; - if (signal (SIGILL, (void(*)(int))SIGWntHandler) == SIG_ERR) - std::cout << "signal(OSD::SetSignal) error\n"; + // Signal handlers will only be used when function ::raise() is called + const int NBSIG = 3; + const int aSignalTypes[NBSIG] = { SIGSEGV, SIGILL, SIGFPE }; + for (int i = 0; i < NBSIG; ++i) + { + typedef void (*SignalFuncType)(int); // same as _crt_signal_t available since vc14 + SignalFuncType aPreviousFunc = SIG_DFL; + if (theSignalMode == OSD_SignalMode_Set || theSignalMode == OSD_SignalMode_SetUnhandled) + { + aPreviousFunc = signal(aSignalTypes[i], (SignalFuncType)SIGWntHandler); + } + if (theSignalMode == OSD_SignalMode_Unset || + (theSignalMode == OSD_SignalMode_SetUnhandled && aPreviousFunc != SIG_DFL && aPreviousFunc != SIG_ERR)) + { + aPreviousFunc = signal(aSignalTypes[i], aPreviousFunc); + } + Standard_ASSERT(aPreviousFunc != SIG_ERR, "signal() failed", std::cout << "OSD::SetSignal(): signal() returns SIG_ERR"); + } // Set Ctrl-C and Ctrl-Break handler fCtrlBrk = Standard_False; #ifndef OCCT_UWP - SetConsoleCtrlHandler (&_osd_ctrl_break_handler, TRUE); -#endif -#ifdef _MSC_VER -// _se_translator_function pOldSeFunc = - _set_se_translator (TranslateSE); + if (theSignalMode == OSD_SignalMode_Set || theSignalMode == OSD_SignalMode_SetUnhandled) + { + SetConsoleCtrlHandler(&_osd_ctrl_break_handler, true); + } + else if (theSignalMode == OSD_SignalMode_Unset) + { + SetConsoleCtrlHandler(&_osd_ctrl_break_handler, false); + } #endif - fFltExceptions = theFloatingSignal; - if (theFloatingSignal) - { - _controlfp (0, _OSD_FPX); // JR add : - } - else { - _controlfp (_OSD_FPX, _OSD_FPX); // JR add : - } -} // end OSD :: SetSignal + SetThreadLocalSignal (theSignalMode, theFloatingSignal); +} //============================================================================ //==== ControlBreak @@ -636,18 +691,12 @@ static Standard_Boolean fCtrlBrk; typedef void (ACT_SIGIO_HANDLER)(void) ; ACT_SIGIO_HANDLER *ADR_ACT_SIGIO_HANDLER = NULL ; -#ifdef DECOSF1 -typedef void (* SIG_PFV) (int); -#endif - #ifdef __GNUC__ # include # include #else # ifdef SA_SIGINFO -# ifndef _AIX # include -# endif # endif #endif typedef void (* SIG_PFV) (int); @@ -658,6 +707,8 @@ typedef void (* SIG_PFV) (int); #include #endif +# define _OSD_FPX (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW) + //============================================================================ //==== Handler //==== Catche the differents signals: @@ -696,11 +747,7 @@ static void Handler (const int theSignal) // std::cout << "OSD::Handler: signal " << (int) theSignal << " occured inside a try block " << std::endl ; if ( ADR_ACT_SIGIO_HANDLER != NULL ) (*ADR_ACT_SIGIO_HANDLER)() ; -#ifdef __linux__ - if (fFltExceptions) - feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW); - //feenableexcept (FE_INVALID | FE_DIVBYZERO); -#endif + sigset_t set; sigemptyset(&set); switch (theSignal) { @@ -746,10 +793,8 @@ static void Handler (const int theSignal) case SIGFPE: sigaddset(&set, SIGFPE); sigprocmask(SIG_UNBLOCK, &set, NULL) ; -#ifdef DECOSF1 - // Pour DEC/OSF1 SIGFPE = Division par zero. - Standard_DivideByZero::NewInstance('')->Jump; - break; +#ifdef __linux__ + OSD::SetFloatingSignal (Standard_True); #endif #if (!defined (__sun)) && (!defined(SOLARIS)) Standard_NumericError::NewInstance("SIGFPE Arithmetic exception detected")->Jump(); @@ -788,12 +833,6 @@ static void Handler (const int theSignal) } #endif break; -#if defined (__sgi) || defined(IRIX) - case SIGTRAP: - sigaddset(&set, SIGTRAP); - sigprocmask(SIG_UNBLOCK, &set, NULL) ; - Standard_DivideByZero::NewInstance("SIGTRAP IntegerDivideByZero")->Jump(); break; -#endif default: #ifdef OCCT_DEBUG std::cout << "Unexpected signal " << theSignal << std::endl ; @@ -814,11 +853,6 @@ static void SegvHandler(const int theSignal, (void)theSignal; // silence GCC warnings (void)theContext; -#ifdef __linux__ - if (fFltExceptions) - feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW); - //feenableexcept (FE_INVALID | FE_DIVBYZERO); -#endif // std::cout << "OSD::SegvHandler activated(SA_SIGINFO)" << std::endl ; if ( ip != NULL ) { sigset_t set; @@ -874,160 +908,127 @@ static void SegvHandler(const int theSignal, #endif +//======================================================================= +//function : SetFloatingSignal +//purpose : +//======================================================================= +void OSD::SetFloatingSignal (Standard_Boolean theFloatingSignal) +{ +#if defined (__linux__) + feclearexcept (FE_ALL_EXCEPT); + if (theFloatingSignal) + { + feenableexcept (_OSD_FPX); + } + else + { + fedisableexcept (_OSD_FPX); + } +#elif defined (__sun) || defined (SOLARIS) + int aSunStat = 0; + sigfpe_handler_type anFpeHandler = (theFloatingSignal ? (sigfpe_handler_type)Handler : NULL); + aSunStat = ieee_handler ("set", "invalid", anFpeHandler); + aSunStat = ieee_handler ("set", "division", anFpeHandler) || aSunStat; + aSunStat = ieee_handler ("set", "overflow", anFpeHandler) || aSunStat; + if (aSunStat) + { +#ifdef OCCT_DEBUG + std::cerr << "ieee_handler does not work !!! KO\n"; +#endif + } +#else + (void)theFloatingSignal; +#endif +} + +//======================================================================= +//function : ToCatchFloatingSignals +//purpose : +//======================================================================= +Standard_Boolean OSD::ToCatchFloatingSignals() +{ +#if defined (__linux__) + return (fegetexcept() & _OSD_FPX) != 0; +#else + return Standard_False; +#endif +} + +//======================================================================= +//function : SetThreadLocalSignal +//purpose : +//======================================================================= +void OSD::SetThreadLocalSignal (OSD_SignalMode /*theSignalMode*/, + Standard_Boolean theFloatingSignal) +{ + SetFloatingSignal (theFloatingSignal); +} + //============================================================================ //==== SetSignal //==== Set the differents signals: //============================================================================ -void OSD::SetSignal(const Standard_Boolean aFloatingSignal) +void OSD::SetSignal (OSD_SignalMode theSignalMode, + Standard_Boolean theFloatingSignal) { - struct sigaction act, oact; - int stat = 0; + SetFloatingSignal (theFloatingSignal); - if( aFloatingSignal ) { - //==== Enable the floating point exceptions =============== -#if defined (__sun) || defined (SOLARIS) - sigfpe_handler_type PHandler = (sigfpe_handler_type) Handler ; - stat = ieee_handler("set", "invalid", PHandler); - stat = ieee_handler("set", "division", PHandler) || stat; - stat = ieee_handler("set", "overflow", PHandler) || stat; + OSD_WasSetSignal = theSignalMode; + if (theSignalMode == OSD_SignalMode_AsIs) + { + return; // nothing to be done with signal handlers + } - //stat = ieee_handler("set", "underflow", PHandler) || stat; - //stat = ieee_handler("set", "inexact", PHandler) || stat; + // Prepare signal descriptors + struct sigaction anActSet, anActDfl, anActOld; + sigemptyset(&anActSet.sa_mask); + sigemptyset(&anActDfl.sa_mask); + sigemptyset(&anActOld.sa_mask); +#ifdef SA_RESTART + anActSet.sa_flags = anActDfl.sa_flags = anActOld.sa_flags = SA_RESTART; +#else + anActSet.sa_flags = anActDfl.sa_flags = anActOld.sa_flags = 0; +#endif +#ifdef SA_SIGINFO + anActSet.sa_flags = anActSet.sa_flags | SA_SIGINFO; + anActSet.sa_sigaction = Handler; +#else + anActSet.sa_handler = Handler; +#endif + anActDfl.sa_handler = SIG_DFL; - if (stat) { -#ifdef OCCT_DEBUG - std::cerr << "ieee_handler does not work !!! KO " << std::endl; + // Set signal handlers; NB: SIGSEGV must be the last one! + const int NBSIG = 8; + const int aSignalTypes[NBSIG] = { SIGFPE, SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGSYS, SIGSEGV }; + for (int i = 0; i < NBSIG; ++i) + { + // SIGSEGV has special handler + if (aSignalTypes[i] == SIGSEGV) + { +#ifdef SA_SIGINFO + anActSet.sa_sigaction = /*(void(*)(int, siginfo_t *, void*))*/ SegvHandler; +#else + anActSet.sa_handler = /*(SIG_PFV)*/ SegvHandler; #endif } -#elif defined (__linux__) - feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW); - fFltExceptions = Standard_True; -#endif + + // set handler according to specified mode and current handler + int retcode = -1; + if (theSignalMode == OSD_SignalMode_Set || theSignalMode == OSD_SignalMode_SetUnhandled) + { + retcode = sigaction (aSignalTypes[i], &anActSet, &anActOld); + } + else if (theSignalMode == OSD_SignalMode_Unset) + { + retcode = sigaction (aSignalTypes[i], &anActDfl, &anActOld); + } + if (theSignalMode == OSD_SignalMode_SetUnhandled && retcode == 0 && anActOld.sa_handler != SIG_DFL) + { + retcode = sigaction (aSignalTypes[i], &anActOld, &anActOld); + } + Standard_ASSERT(retcode == 0, "sigaction() failed", std::cout << "OSD::SetSignal(): sigaction() failed for " << aSignalTypes[i] << std::endl); } - else - { -#if defined (__linux__) - fedisableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW); - fFltExceptions = Standard_False; -#endif - } - -#if defined (sgi) || defined (IRIX ) - char *TRAP_FPE = getenv("TRAP_FPE") ; - if ( TRAP_FPE == NULL ) { -#ifdef OCCT_DEBUG - std::cout << "On SGI you must set TRAP_FPE environment variable : " << std::endl ; - std::cout << "set env(TRAP_FPE) \"UNDERFL=FLUSH_ZERO;OVERFL=DEFAULT;DIVZERO=DEFAULT;INT_OVERFL=DEFAULT\" or" << std::endl ; - std::cout << "setenv TRAP_FPE \"UNDERFL=FLUSH_ZERO;OVERFL=DEFAULT;DIVZERO=DEFAULT;INT_OVERFL=DEFAULT\"" << std::endl ; -#endif - } -#endif - - //==== Save the old Signal Handler, and set the new one =================== - - sigemptyset(&act.sa_mask) ; - -#ifdef SA_RESTART - act.sa_flags = SA_RESTART ; -#else - act.sa_flags = 0 ; -#endif -#ifdef SA_SIGINFO - act.sa_flags = act.sa_flags | SA_SIGINFO ; - act.sa_sigaction = /*(void(*)(int, siginfo_t *, void*))*/ Handler; -#else - act.sa_handler = /*(SIG_PFV)*/ Handler; -#endif - - //==== Always detected the signal "SIGFPE" ================================= - stat = sigaction(SIGFPE,&act,&oact); // ...... floating point exception - if (stat) { -#ifdef OCCT_DEBUG - std::cerr << "sigaction does not work !!! KO " << std::endl; -#endif - perror("sigaction "); - } - - //==== Detected the only the "free" signals ================================ - sigaction(SIGHUP,&act,&oact); // ...... hangup - -#ifdef OBJS - if(oact.sa_handler) - sigaction(SIGHUP,&oact,&oact); -#endif - - sigaction(SIGINT,&act,&oact); // ...... interrupt - -#ifdef OBJS - if(oact.sa_handler) - sigaction(SIGINT,&oact,&oact); -#endif - - sigaction(SIGQUIT,&act,&oact); // ...... quit - -#ifdef OBJS - if(oact.sa_handler) - sigaction(SIGQUIT,&oact,&oact); -#endif - - sigaction(SIGILL,&act,&oact); // ...... illegal instruction - -#ifdef OBJS - if(oact.sa_handler) - sigaction(SIGILL,&oact,&oact); -#endif - - sigaction(SIGBUS,&act,&oact); // ...... bus error - -#ifdef OBJS - if(oact.sa_handler) - sigaction(SIGBUS,&oact,&oact); -#endif - -#if !defined(__linux__) - sigaction(SIGSYS,&act,&oact); // ...... bad argument to system call - -# ifdef OBJS - if(oact.sa_handler) - sigaction(SIGSYS,&oact,&oact); -# endif -#endif - -#if defined (__sgi) || defined(IRIX) - sigaction(SIGTRAP,&act,&oact); // Integer Divide By Zero (IRIX) - -# ifdef OBJS - if(oact.sa_handler) - sigaction(SIGTRAP,&oact,&oact); -# endif -#endif - -#ifdef SA_SIGINFO - act.sa_sigaction = /*(void(*)(int, siginfo_t *, void*))*/ SegvHandler; -#else - act.sa_handler = /*(SIG_PFV)*/ SegvHandler; -#endif - - if ( sigaction( SIGSEGV , &act , &oact ) ) // ...... segmentation violation - perror("OSD::SetSignal sigaction( SIGSEGV , &act , &oact ) ") ; - -#ifdef OBJS - if(oact.sa_handler) - sigaction(SIGSEGV,&oact,&oact); -#endif -#if defined(__osf__) || defined(DECOSF1) - struct sigaction action, prev_action; - action.sa_handler = SIG_IGN; - action.sa_mask = 0; - action.sa_flags = 0; - - if (sigaction (SIGFPE, &action, &prev_action) == -1) { - perror ("sigaction"); - exit (1); - } -#endif - } //============================================================================ diff --git a/src/QABugs/QABugs_11.cxx b/src/QABugs/QABugs_11.cxx index 1e97855a9d..983cc5a45f 100644 --- a/src/QABugs/QABugs_11.cxx +++ b/src/QABugs/QABugs_11.cxx @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -2458,6 +2459,68 @@ static Standard_Integer OCC6143 (Draw_Interpretor& di, Standard_Integer argc, co return 0; } + +//! Auxiliary functor. +struct TestParallelFunctor +{ + TestParallelFunctor() : myNbNotRaised (0), myNbSigSegv (0), myNbUnknown (0) {} + + Standard_Integer NbNotRaised() const { return myNbNotRaised; } + Standard_Integer NbSigSegv() const { return myNbSigSegv; } + Standard_Integer NbUnknown() const { return myNbUnknown; } + + void operator() (int theThreadId, int theTaskId) const + { + (void )theThreadId; + (void )theTaskId; + + // Test Access Violation + { + try { + OCC_CATCH_SIGNALS + int* pint = NULL; + *pint = 4; + Standard_Atomic_Increment (&myNbNotRaised); + } + #ifdef _WIN32 + catch (OSD_Exception_ACCESS_VIOLATION const&) + #else + catch (OSD_SIGSEGV const&) + #endif + { + Standard_Atomic_Increment (&myNbSigSegv); + } + catch (Standard_Failure const& ) + { + Standard_Atomic_Increment (&myNbUnknown); + } + } + } +private: + mutable volatile Standard_Integer myNbNotRaised; + mutable volatile Standard_Integer myNbSigSegv; + mutable volatile Standard_Integer myNbUnknown; +}; + +static Standard_Integer OCC30775 (Draw_Interpretor& theDI, Standard_Integer theNbArgs, const char** ) +{ + if (theNbArgs != 1) + { + std::cout << "Syntax error: wrong number of arguments\n"; + return 1; + } + + Handle(OSD_ThreadPool) aPool = new OSD_ThreadPool (4); + OSD_ThreadPool::Launcher aLauncher (*aPool, 4); + TestParallelFunctor aFunctor; + aLauncher.Perform (0, 100, aFunctor); + theDI << "NbRaised: " << (aFunctor.NbSigSegv() + aFunctor.NbUnknown()) << "\n" + << "NbNotRaised: " << aFunctor.NbNotRaised() << "\n" + << "NbSigSeg: " << aFunctor.NbSigSegv() << "\n" + << "NbUnknown: " << aFunctor.NbUnknown() << "\n"; + return 0; +} + #if defined(_MSC_VER) #pragma optimize( "", on ) #endif @@ -4816,7 +4879,8 @@ void QABugs::Commands_11(Draw_Interpretor& theCommands) { theCommands.Add("OCC5739", "OCC5739 name shape step", __FILE__, OCC5739_UniAbs, group); theCommands.Add("OCC6046", "OCC6046 nb_of_vectors size", __FILE__, OCC6046, group); theCommands.Add("OCC5698", "OCC5698 wire", __FILE__, OCC5698, group); - theCommands.Add("OCC6143", "OCC6143", __FILE__, OCC6143, group); + theCommands.Add("OCC6143", "OCC6143 catching signals", __FILE__, OCC6143, group); + theCommands.Add("OCC30775", "OCC30775 catching signals in threads", __FILE__, OCC30775, group); theCommands.Add("OCC7141", "OCC7141 [nCount] aPath", __FILE__, OCC7141, group); theCommands.Add("OCC7372", "OCC7372", __FILE__, OCC7372, group); theCommands.Add("OCC8169", "OCC8169 edge1 edge2 plane", __FILE__, OCC8169, group); diff --git a/tests/bugs/fclasses/bug30775 b/tests/bugs/fclasses/bug30775 new file mode 100644 index 0000000000..706babd788 --- /dev/null +++ b/tests/bugs/fclasses/bug30775 @@ -0,0 +1,24 @@ +puts "================" +puts "0030775: OSD::SetSignal() within OSD_ThreadPool should not override global handlers" +puts "================" +puts "" + +pload QAcommands + +dsetsignal set + +set IsDone [catch {set aResult [OCC30775]} result] + +if { ${IsDone} != 0 } { + puts "result = ${result}" + puts "Error: command raised exception" +} else { + if { [string first "NbRaised: 100" $aResult] != -1 } { + puts "OK test case" + } else { + puts "Error: expected to have 100 raised expections" + } +} + +# restore defaults +dsetsignal \ No newline at end of file