μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
SigBusGuard.cpp
Go to the documentation of this file.
1/*
2---------------------------------------------------------------------------
3
4 This file is part of uHAL.
5
6 uHAL is a hardware access library and programming framework
7 originally developed for upgrades of the Level-1 trigger of the CMS
8 experiment at CERN.
9
10 uHAL is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 uHAL is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with uHAL. If not, see <http://www.gnu.org/licenses/>.
22
23
24 Tom Williams, Rutherford Appleton Laboratory
25 email: tom.williams <AT> cern.ch
26
27 Dan Gastler, Boston University
28 email: dgastler <AT> bu.edu
29
30---------------------------------------------------------------------------
31*/
32
33#include "uhal/SigBusGuard.hpp"
34
35
36#include <string.h>
37
38#include "uhal/log/log.hpp"
39
40
41namespace uhal {
42
43 std::mutex SigBusGuard::sMutex;
44 sigjmp_buf SigBusGuard::sEnv;
45 volatile sig_atomic_t SigBusGuard::sProtected = 0;
46
48 mLockGuard(sMutex),
49 mAction({0}),
50 mOriginalAction({0})
51 {
52 // 1) Register our signal handler for SIGBUS, saving original in mOriginalAction
53 log(Debug(), "Registering uHAL SIGBUS handler");
54 mAction.sa_handler = SigBusGuard::handle;
55 sigemptyset(&mAction.sa_mask);
56 if (sigaction(SIGBUS, &mAction, &mOriginalAction) != 0) {
57 exception::SignalHandlerNotRegistered lExc;
58 log(lExc, "Failed to register SIGBUS handler (in SigBusGuard constructor); errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
59 throw lExc;
60 }
61
62 // 2) Update this thread's signal mask to unblock SIGBUS (and throw if already unblocked)
63 sigset_t lMaskedSignals;
64 sigfillset(&lMaskedSignals);
65 sigdelset(&lMaskedSignals, SIGKILL); // Unblockable
66 sigdelset(&lMaskedSignals, SIGSTOP); // Unblockable
67 sigdelset(&lMaskedSignals, SIGINT); // Ctrl+C
68 sigdelset(&lMaskedSignals, SIGBUS);
69 const int lErrNo = pthread_sigmask(SIG_SETMASK, &lMaskedSignals, &mOriginalMask);
70 if (lErrNo != 0) {
71 exception::SignalMaskingFailure lExc;
72 log(lExc, "Failed to update signal mask in SigBusGuard constructor; errno=", Integer(lErrNo), ", meaning ", Quote (strerror(lErrNo)));
73 throw lExc;
74 }
75 if (sigismember(&mOriginalMask, SIGBUS) != 1) {
76 exception::SignalNotBlocked lExc;
77 log(lExc, "SIGBUS must be blocked (by all threads) before using SigBusGuard");
78 throw lExc;
79 }
80 }
81
82
84 {
85 // 1) Restore the original signal handler for SIGBUS
86 if (sigaction(SIGBUS, &mOriginalAction, NULL) != 0)
87 log(Error(), "Failed to re-register old SIGBUS handler (in SigBusGuard destructor); errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
88 else
89 log(Debug(), "Restored original SIGBUS handler");
90
91 // 2) Update this thread's signal mask to block SIGBUS again
92 const int lErrNo = pthread_sigmask(SIG_SETMASK, &mOriginalMask, NULL);
93 if (lErrNo != 0)
94 log(Error(), "Failed to update signal mask in SigBusGuard constructor; errno=", Integer(lErrNo), ", meaning ", Quote (strerror(lErrNo)));
95 }
96
97
98 void SigBusGuard::protect(const std::function<void()>& aAccess, const std::string& aMessage)
99 {
100 sProtected = 1;
101
102 // First time sigsetjmp is called it just stores the context of where it is called
103 // and returns 0. If a signal is received and siglongjmp is called in the handler,
104 // then the thread will return here and sigsetjmp will then return that signal
105 // NOTE: HW access must wrapped in a function and invoked in this function because if
106 // siglongjmp is called then it must be called before function containing sigsetjmp returns
107 if (SIGBUS == sigsetjmp(sEnv,1)) {
108 // Raise exception with supplied message if SIGBUS received
109 sProtected = 0;
110 exception::SigBusError lException;
111 log (lException, aMessage);
112 throw lException;
113 }
114 else
115 aAccess();
116
117 sProtected = 0;
118 }
119
120
122 {
123 sigset_t lSigSet;
124 sigemptyset(&lSigSet);
125 sigaddset(&lSigSet, SIGBUS);
126 const int lErrNo = pthread_sigmask(SIG_BLOCK, &lSigSet, NULL);
127 if (lErrNo != 0) {
128 exception::SignalMaskingFailure lExc;
129 log(lExc, "Failed to update signal mask; errno=", Integer(lErrNo), ", meaning ", Quote (strerror(lErrNo)));
130 throw lExc;
131 }
132 }
133
134
135 void SigBusGuard::handle(int aSignal)
136 {
137 // Warn users if SIGBUS raised outside of 'protect' function, as indicates that the offending code
138 // needs to be modified so that offending line is wrapped by the 'protect' function
139 if (sProtected == 0) {
140 char message[] = "WARNING: A uHAL SigBusGuard has been constructed but SIGBUS was received outside of the 'protect' method. This will cause *undefined behaviour*.\nAfter creating a uhal::SigBusGuard instance, you must run any code that can raise SIGBUS inside the SigBusGuard::protect method (using its std::function argument).\n";
141 write(STDOUT_FILENO, message, strlen(message));
142 }
143
144 // Jump back to the point in the stack described by sEnv (as set by sigsetjmp), with sigsetjmp now returning SIGBUS
145 if (aSignal == SIGBUS)
146 siglongjmp(sEnv, aSignal);
147 }
148
149}
sigset_t mOriginalMask
Definition: SigBusGuard.hpp:73
void protect(const std::function< void()> &, const std::string &)
Definition: SigBusGuard.cpp:98
static sigjmp_buf sEnv
Definition: SigBusGuard.hpp:76
struct sigaction mOriginalAction
Definition: SigBusGuard.hpp:72
static void blockSIGBUS()
static volatile sig_atomic_t sProtected
Definition: SigBusGuard.hpp:77
static std::mutex sMutex
Definition: SigBusGuard.hpp:75
static void handle(int)
DebugLevel Debug
Definition: LogLevels.cpp:133
_Quote< T > Quote(const T &aT)
_Integer< T, IntFmt<> > Integer(const T &aT)
Forward declare a function which creates an instance of the ultra-lightweight wrapper from an integer...
string message
Definition: __init__.py:7
ErrorLevel Error
Definition: LogLevels.cpp:61
void log(FatalLevel &aFatal, const T0 &aArg0)
Function to add a log entry at Fatal level.
Definition: log.hxx:18