ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
fe_trap.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2024 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <config/config.hpp>
23
24#ifdef FPE
25
26#include <memory>
27#include <mutex>
28#include <optional>
29#include <utility>
30
31/**
32 * @brief Floating-point exception trap.
33 *
34 * Thread-safe RAII-style mechanism to trap floating-point exceptions
35 * (on platforms that support floating-point environment management) for
36 * the duration of a scoped block. Exception traps are set when the object
37 * is created; when getting out-of-scope, either normally or during stack
38 * unwinding, the exception traps are automatically reset.
39 *
40 * Please note "exception" and "exception handling" have a specific meaning
41 * in this context and are completely unrelated to C++ exceptions.
42 * For more details, see annex F IEC 60559 "floating-point arithmetic"
43 * in ISO/EIC 9899 @cite ISO-EIC-9899-1999 and chapter 7
44 * "Exceptions and default exception handling" in
45 * ISO/IEC 60559:2020(E) @cite ISO-EIC-60559-2020.
46 *
47 * The exception handling behavior is implementation-defined. For example,
48 * GNU libc sends the @c SIGFPE signal on x86 architectures; it can be caught
49 * by a signal handler that leverages stack environments and long jumps via
50 * [`sigsetjmp()`](https://www.man7.org/linux/man-pages/man3/sigsetjmp.3.html).
51 * On Armv8, trapping is controlled by FPCR flags; for more details,
52 * see section C5.2.8 "FPCR, Floating-point Control Register" in the Armv8
53 * manual @cite ARM-DDI-0487-2024. AppleClang sends the @c SIGILL signal
54 * on Apple Silicon architectures.
55 *
56 * A modified singleton pattern is leveraged to guarantee only one trap is
57 * active at any time; once expired, a new trap can be instantiated.
58 * The @ref make_unique_scoped function returns a wrapper object whose
59 * lifetime determines the trap duration. To set a trap in a recursive
60 * function, use @ref make_shared_scoped instead.
61 *
62 * Usage:
63 * @code{.cpp}
64 * #include <instrumentation/fe_trap.hpp>
65 * #include <cmath>
66 * int main() {
67 * auto volatile zero = 0.;
68 * auto value = 1. / zero; // generate NaN
69 * {
70 * auto trap = fe_trap::make_unique_scoped();
71 * value = 1. / zero; // execution flow should be interrupted here
72 * }
73 * value = 1. / zero; // generate NaN
74 * return std::isnan(value) ? 0 : 1;
75 * }
76 * @endcode
77 * Build the code without fast-math and without any optimization
78 * (optimizations always assume divisions by zero cannot happen):
79 * @code{.sh}
80 * g++ -std=c++20 -O0 -fno-fast-math -I../src/instrumentation/include \
81 * -I../src/config/include -Isrc/config/include \
82 * main.cpp ../src/instrumentation/src/fe_trap.cpp
83 * ./a.out
84 * }
85 * @endcode
86 */
87class fe_trap {
88 struct global_state_params {
89 std::weak_ptr<fe_trap> observer;
90 std::mutex mutex;
91 };
92 static global_state_params global_state;
93
94 struct scoped_instance {
95 explicit scoped_instance(std::shared_ptr<fe_trap> ptr)
96 : m_resource{std::move(ptr)} {}
97 scoped_instance(scoped_instance const &) = delete;
98 scoped_instance(scoped_instance &&) noexcept = default;
99 scoped_instance &operator=(scoped_instance const &) = delete;
100 scoped_instance &operator=(scoped_instance &&) noexcept = default;
101 bool is_unique() const { return m_resource->is_unique(); }
102 int get_flags() const { return m_resource->get_flags(); }
103
104 private:
105 std::shared_ptr<fe_trap> m_resource;
106 };
107
108 struct deleter {
109 void operator()(fe_trap *ptr) { delete ptr; }
110 };
111 friend deleter;
112
113 int m_flags;
114 bool m_unique;
115
116 fe_trap(std::optional<int> excepts, bool unique);
117 ~fe_trap();
118
119 static int parse_excepts(std::optional<int> excepts);
120
121public:
122 fe_trap(fe_trap const &) = delete;
123 fe_trap(fe_trap &&) noexcept = delete;
124 fe_trap &operator=(fe_trap const &) = delete;
125 fe_trap &operator=(fe_trap &&) noexcept = delete;
126 /** @brief Get floating-point exception flags. */
127 int get_flags() const { return m_flags; }
128 /** @brief Check if this handle is a unique handle. */
129 bool is_unique() const { return m_unique; }
130
131 /**
132 * @brief Generate a unique trap with the lifetime of the current scope.
133 * @param excepts Combination of floating-point exception flags.
134 */
135 static scoped_instance
136 make_unique_scoped(std::optional<int> excepts = std::nullopt);
137 /**
138 * @brief Generate a shared trap with the lifetime of the current scope.
139 * Subsequent calls to this function will yield the same trap handle,
140 * as long as they have the same parameter @c excepts.
141 * @param excepts Combination of floating-point exception flags.
142 */
143 static scoped_instance
144 make_shared_scoped(std::optional<int> excepts = std::nullopt);
145};
146
147#endif // FPE
Floating-point exception trap.
Definition fe_trap.hpp:87
bool is_unique() const
Check if this handle is a unique handle.
Definition fe_trap.hpp:129
fe_trap(fe_trap const &)=delete
fe_trap(fe_trap &&) noexcept=delete
static scoped_instance make_shared_scoped(std::optional< int > excepts=std::nullopt)
Generate a shared trap with the lifetime of the current scope.
Definition fe_trap.cpp:121
int get_flags() const
Get floating-point exception flags.
Definition fe_trap.hpp:127
static scoped_instance make_unique_scoped(std::optional< int > excepts=std::nullopt)
Generate a unique trap with the lifetime of the current scope.
Definition fe_trap.cpp:109
This file contains the defaults for ESPResSo.