0030775: Foundation Classes - Preserve application-defined top-level exception filter
authorkgv <kgv@opencascade.com>
Mon, 17 Jun 2019 15:42:36 +0000 (18:42 +0300)
committerabv <abv@opencascade.com>
Sat, 14 Sep 2019 08:45:34 +0000 (11:45 +0300)
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
src/Media/Media_PlayerContext.cxx
src/OSD/FILES
src/OSD/OSD.hxx
src/OSD/OSD_SignalMode.hxx [new file with mode: 0644]
src/OSD/OSD_ThreadPool.cxx
src/OSD/OSD_signal.cxx
src/QABugs/QABugs_11.cxx
tests/bugs/fclasses/bug30775 [new file with mode: 0644]

index 8c57448..2ef68c3 100644 (file)
@@ -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)
+  {
+    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(aMode, aSetFPE);
+
+  // report actual status in the end
+  const char* aModeStr = 0;
+  switch (OSD::SignalMode())
   {
-    OSD_Environment aEnv ("CSF_FPE");
-    TCollection_AsciiString aEnvStr = aEnv.Value();
-    setFPE = (! aEnvStr.IsEmpty() && aEnvStr.Value(1) != '0');
+  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;
   }
-  OSD::SetSignal (setFPE);
-  theDI << "Signal handlers are set, with FPE " << (setFPE ? "armed" : "disarmed"); 
+  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",
index 537d293..a49b44b 100644 (file)
@@ -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 (;;)
index 488da0c..d7615db 100755 (executable)
@@ -93,3 +93,4 @@ OSD_Timer.hxx
 OSD_WhoAmI.hxx
 OSD_WNT.cxx
 OSD_WNT.hxx
+OSD_SignalMode.hxx
index dd80e02..03dc1ee 100644 (file)
 #define _OSD_HeaderFile
 
 #include <Standard.hxx>
-#include <Standard_DefineAlloc.hxx>
-#include <Standard_Handle.hxx>
-
-#include <Standard_Boolean.hxx>
-#include <Standard_Integer.hxx>
-#include <Standard_Real.hxx>
 #include <Standard_PCharacter.hxx>
-#include <Standard_CString.hxx>
-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 <OSD_SignalMode.hxx>
 
-//! 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
-  //! <tt>_EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW</tt> 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
-  //! <tt>FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW</tt> (in Linux conventions).<br>
-  //! 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.
+  //!
+  //! 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.
   //!
-  //! 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.
+  //! 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.
   //!
-  Standard_EXPORT static void SetSignal (const Standard_Boolean theFloatingSignal = Standard_True);
+  //! 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);
+
+  //! 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();
 
-  //! Return floating signal catching value previously set by SetSignal().
+  //! 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 (file)
index 0000000..b220b04
--- /dev/null
@@ -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
index 22c6877..0d3bc2b 100644 (file)
@@ -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;
     }
index 1b712de..654523a 100644 (file)
 #include <OSD_Exception_CTRL_BREAK.hxx>
 #include <Standard_DivideByZero.hxx>
 #include <Standard_Overflow.hxx>
+#include <Standard_Assert.hxx>
 
-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 ;
   }
+#endif
 
   char aBufferA[2048];
   WideCharToMultiByte(CP_UTF8, 0, buffer, -1, aBufferA, sizeof(aBufferA), NULL, NULL);
@@ -368,12 +360,59 @@ static LONG WINAPI WntHandler (EXCEPTION_POINTERS *lpXP)
 }
 
 //=======================================================================
+//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);
-#endif
-
-  fFltExceptions = theFloatingSignal;
-  if (theFloatingSignal)
+  if (theSignalMode == OSD_SignalMode_Set || theSignalMode == OSD_SignalMode_SetUnhandled)
   {
-    _controlfp (0, _OSD_FPX);        // JR add :
+    SetConsoleCtrlHandler(&_osd_ctrl_break_handler, true);
   }
-  else {
-    _controlfp (_OSD_FPX, _OSD_FPX); // JR add :
+  else if (theSignalMode == OSD_SignalMode_Unset)
+  {
+    SetConsoleCtrlHandler(&_osd_ctrl_break_handler, false);
   }
-}  // end OSD :: SetSignal
+#endif
+
+  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 <stdlib.h>
 # include <stdio.h>
 #else
 #  ifdef SA_SIGINFO
-#    ifndef _AIX
 #  include <sys/siginfo.h>
-#     endif
 #  endif
 #endif
 typedef void (* SIG_PFV) (int);
@@ -658,6 +707,8 @@ typedef void (* SIG_PFV) (int);
   #include <sys/signal.h>
 #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
 
-//============================================================================
-//==== SetSignal
-//====     Set the differents signals:
-//============================================================================
-
-void OSD::SetSignal(const Standard_Boolean aFloatingSignal)
+//=======================================================================
+//function : SetFloatingSignal
+//purpose  :
+//=======================================================================
+void OSD::SetFloatingSignal (Standard_Boolean theFloatingSignal)
 {
-  struct sigaction act, oact;
-  int              stat = 0;
-
-  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;
-
-    //stat = ieee_handler("set", "underflow", PHandler) || stat;
-    //stat = ieee_handler("set", "inexact", PHandler) || stat;
-
-    if (stat) {
-#ifdef OCCT_DEBUG
-      std::cerr << "ieee_handler does not work !!! KO " << std::endl;
-#endif
-    }
-#elif defined (__linux__)
-    feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
-    fFltExceptions = Standard_True;
-#endif
+#if defined (__linux__)
+  feclearexcept (FE_ALL_EXCEPT);
+  if (theFloatingSignal)
+  {
+    feenableexcept (_OSD_FPX);
   }
   else
   {
-#if defined (__linux__)
-    fedisableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
-    fFltExceptions = Standard_False;
-#endif
+    fedisableexcept (_OSD_FPX);
   }
-
-#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) {
+#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 << "sigaction does not work !!! KO " << std::endl;
+    std::cerr << "ieee_handler does not work !!! KO\n";
 #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);
+#else
+  (void)theFloatingSignal;
 #endif
+}
 
-  sigaction(SIGILL,&act,&oact);   // ...... illegal instruction
-
-#ifdef OBJS
-  if(oact.sa_handler)
-       sigaction(SIGILL,&oact,&oact);
+//=======================================================================
+//function : ToCatchFloatingSignals
+//purpose  :
+//=======================================================================
+Standard_Boolean OSD::ToCatchFloatingSignals()
+{
+#if defined (__linux__)
+  return (fegetexcept() & _OSD_FPX) != 0;
+#else
+  return Standard_False;
 #endif
+}
 
-  sigaction(SIGBUS,&act,&oact);   // ...... bus error
-
-#ifdef OBJS
-  if(oact.sa_handler)
-       sigaction(SIGBUS,&oact,&oact);
-#endif
+//=======================================================================
+//function : SetThreadLocalSignal
+//purpose  :
+//=======================================================================
+void OSD::SetThreadLocalSignal (OSD_SignalMode /*theSignalMode*/,
+                                Standard_Boolean theFloatingSignal)
+{
+  SetFloatingSignal (theFloatingSignal);
+}
 
-#if !defined(__linux__)
-  sigaction(SIGSYS,&act,&oact);   // ...... bad argument to system call
+//============================================================================
+//==== SetSignal
+//====     Set the differents signals:
+//============================================================================
 
-# ifdef OBJS
-  if(oact.sa_handler)
-       sigaction(SIGSYS,&oact,&oact);
-# endif
-#endif
+void OSD::SetSignal (OSD_SignalMode theSignalMode,
+                     Standard_Boolean theFloatingSignal)
+{
+  SetFloatingSignal (theFloatingSignal);
 
-#if defined (__sgi) || defined(IRIX)
-  sigaction(SIGTRAP,&act,&oact);   // Integer Divide By Zero (IRIX)
+  OSD_WasSetSignal = theSignalMode;
+  if (theSignalMode == OSD_SignalMode_AsIs)
+  {
+    return; // nothing to be done with signal handlers
+  }
 
-# ifdef OBJS
-  if(oact.sa_handler)
-       sigaction(SIGTRAP,&oact,&oact);
-# endif
+  // 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
-  act.sa_sigaction = /*(void(*)(int, siginfo_t *, void*))*/ SegvHandler;
+  anActSet.sa_flags = anActSet.sa_flags | SA_SIGINFO;
+  anActSet.sa_sigaction = Handler;
 #else
-  act.sa_handler = /*(SIG_PFV)*/ SegvHandler;
+  anActSet.sa_handler = Handler;
 #endif
+  anActDfl.sa_handler = SIG_DFL;
 
-  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);
-   }
+  // 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
+    }
 
+    // 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);
+  }
 }
 
 //============================================================================
index 1e97855..983cc5a 100644 (file)
@@ -55,6 +55,7 @@
 #include <OSD_Exception_ACCESS_VIOLATION.hxx>
 #include <OSD_Exception_STACK_OVERFLOW.hxx>
 #include <OSD.hxx>
+#include <OSD_ThreadPool.hxx>
 #include <STEPCAFControl_Writer.hxx>
 #include <STEPControl_StepModelType.hxx>
 #include <Interface_Static.hxx>
@@ -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 (file)
index 0000000..706babd
--- /dev/null
@@ -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