KiCad PCB EDA Suite
Loading...
Searching...
No Matches
navlib_safe_init.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <navlib_safe_init.h>
21
22#include <wx/log.h>
23
24#include <atomic>
25#include <exception>
26#include <mutex>
27
28#if !defined( __WINDOWS__ )
29#include <signal.h>
30#include <setjmp.h>
31#endif
32
33static std::atomic<bool> s_driverCrashed( false );
34
35#if !defined( __WINDOWS__ )
36
37// SIGABRT disposition and the recovery jump buffer are process-wide, so concurrent
38// initialization attempts must be serialized. The saved disposition uses static storage
39// because an automatic local modified after sigsetjmp() is indeterminate after siglongjmp().
40static std::mutex s_initMutex;
41static sigjmp_buf s_jumpBuffer;
42static struct sigaction s_savedSigAbrtAction;
43
44static void navlibSigAbrtHandler( int aSignal )
45{
46 siglongjmp( s_jumpBuffer, 1 );
47}
48
49#endif
50
51
52bool SafeNavlibInit( const std::function<void()>& aInitFunc )
53{
54 if( s_driverCrashed.load() )
55 return false;
56
57#if !defined( __WINDOWS__ )
58 // Install a temporary SIGABRT handler to recover from abort() calls triggered by
59 // buggy 3Dconnexion drivers. The driver on some macOS versions calls std::terminate()
60 // from a noexcept context inside NlCreate, which calls abort(). By catching SIGABRT
61 // we can recover instead of crashing the entire application.
62 std::lock_guard<std::mutex> lock( s_initMutex );
63
64 // Re-check under the lock in case a concurrent caller latched the crash flag while we
65 // waited on the mutex.
66 if( s_driverCrashed.load() )
67 return false;
68
69 struct sigaction newAction;
70
71 newAction.sa_handler = navlibSigAbrtHandler;
72 sigemptyset( &newAction.sa_mask );
73 newAction.sa_flags = 0;
74
75 // Arm the jump buffer before installing the handler so an abort() that fires the
76 // instant the handler is active always lands here with a valid buffer.
77 if( sigsetjmp( s_jumpBuffer, 1 ) != 0 )
78 {
79 // We got here via siglongjmp from the SIGABRT handler.
80 sigaction( SIGABRT, &s_savedSigAbrtAction, nullptr );
81 s_driverCrashed.store( true );
82 wxLogWarning( wxT( "3Dconnexion driver crashed during initialization. "
83 "SpaceMouse support will be disabled for this session." ) );
84 return false;
85 }
86
87 sigaction( SIGABRT, &newAction, &s_savedSigAbrtAction );
88
89 try
90 {
91 aInitFunc();
92 sigaction( SIGABRT, &s_savedSigAbrtAction, nullptr );
93 return true;
94 }
95 catch( const std::exception& e )
96 {
97 sigaction( SIGABRT, &s_savedSigAbrtAction, nullptr );
98 wxLogTrace( wxT( "KI_TRACE_NAVLIB" ),
99 wxT( "3Dconnexion initialization failed: %s" ), e.what() );
100 return false;
101 }
102 catch( ... )
103 {
104 sigaction( SIGABRT, &s_savedSigAbrtAction, nullptr );
105 wxLogTrace( wxT( "KI_TRACE_NAVLIB" ),
106 wxT( "3Dconnexion initialization failed with unknown exception" ) );
107 return false;
108 }
109#else
110 // On Windows, VEH can handle structured exceptions but std::terminate from a noexcept
111 // context is not recoverable. We still catch C++ exceptions.
112 try
113 {
114 aInitFunc();
115 return true;
116 }
117 catch( const std::exception& e )
118 {
119 wxLogTrace( wxT( "KI_TRACE_NAVLIB" ),
120 wxT( "3Dconnexion initialization failed: %s" ), e.what() );
121 return false;
122 }
123 catch( ... )
124 {
125 wxLogTrace( wxT( "KI_TRACE_NAVLIB" ),
126 wxT( "3Dconnexion initialization failed with unknown exception" ) );
127 return false;
128 }
129#endif
130}
131
132
134{
135 return s_driverCrashed.load();
136}
static std::mutex s_initMutex
static sigjmp_buf s_jumpBuffer
static void navlibSigAbrtHandler(int aSignal)
bool SafeNavlibInit(const std::function< void()> &aInitFunc)
Attempt to run the given function, recovering from both C++ exceptions and abort() calls triggered by...
bool NavlibDriverCrashed()
Returns true if a previous SafeNavlibInit call detected a driver crash.
static std::atomic< bool > s_driverCrashed(false)
static struct sigaction s_savedSigAbrtAction
Safe initialization wrapper for the 3Dconnexion navlib SDK.