ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
fe_trap.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2024-2026 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#include <config/config.hpp>
21
22#ifdef ESPRESSO_FPE
23
25
26#include <cassert>
27#include <cfenv>
28#include <memory>
29#include <mutex>
30#include <optional>
31#include <stdexcept>
32#include <thread>
33
34#if defined(__STDC_IEC_559__) and defined(__GLIBC__) and defined(__x86_64__)
35#define ESPRESSO_FPE_USING_GLIBC_X86_64
36#elif defined(__arm64__) and defined(__APPLE__)
37#define ESPRESSO_FPE_USING_APPLE_ARM_64
38#endif
39
40fe_trap::global_state_params fe_trap::global_state{{}, {}};
41
43 if (m_active) {
44 return;
45 }
46#if defined(ESPRESSO_FPE_USING_GLIBC_X86_64)
47 {
48 [[maybe_unused]] auto const status = feenableexcept(m_flags);
49 // note: status should be 0 since we use the singleton pattern
50 assert(status == 0);
51 }
52#elif defined(ESPRESSO_FPE_USING_APPLE_ARM_64)
53 {
54 using fpcr_t = decltype(std::fenv_t::__fpcr);
55 std::fenv_t env;
56 {
57 [[maybe_unused]] auto const status = std::fegetenv(&env);
58 assert(status == 0u);
59 }
60 env.__fpcr |= static_cast<fpcr_t>(m_flags);
61 {
62 [[maybe_unused]] auto const status = std::fesetenv(&env);
63 assert(status == 0u);
64 }
65 }
66#else
67#error "FE not supported"
68#endif
69 m_active = true;
70}
71
73 if (not m_active) {
74 return;
75 }
76#if defined(ESPRESSO_FPE_USING_GLIBC_X86_64)
77 {
78 [[maybe_unused]] auto const status = fedisableexcept(m_flags);
79 // note: status can become 0 in a signal handler that calls @c siglongjmp()
80 assert(status == 0 or status == m_flags);
81 }
82#elif defined(ESPRESSO_FPE_USING_APPLE_ARM_64)
83 {
84 using fpcr_t = decltype(std::fenv_t::__fpcr);
85 std::fenv_t env;
86 {
87 [[maybe_unused]] auto const status = std::fegetenv(&env);
88 assert(status == 0u);
89 }
90 assert((env.__fpcr & static_cast<fpcr_t>(m_flags)) ==
91 static_cast<fpcr_t>(m_flags));
92 env.__fpcr &= static_cast<fpcr_t>(~m_flags);
93 {
94 [[maybe_unused]] auto const status = std::fesetenv(&env);
95 assert(status == 0u);
96 }
97 }
98#else
99#error "FE not supported"
100#endif
101 m_active = false;
102}
103
104int fe_trap::parse_excepts(std::optional<int> excepts) {
105 auto const fallback = FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW;
106 int retval = excepts ? *excepts : fallback;
107#if defined(ESPRESSO_FPE_USING_APPLE_ARM_64)
108 retval <<= 8;
109#endif
110 return retval;
111}
112
113fe_trap::scoped_instance
114fe_trap::make_unique_scoped(std::optional<int> excepts) {
115 std::lock_guard<std::mutex> lock(fe_trap::global_state.mutex);
116 if (fe_trap::global_state.observer.lock()) {
117 throw std::runtime_error("Cannot create more than 1 instance of fe_trap");
118 }
119 auto raw_ptr = new fe_trap(excepts, true);
120 auto watched = std::shared_ptr<fe_trap>(raw_ptr, deleter{});
121 fe_trap::global_state.observer = watched;
122 return fe_trap::scoped_instance(watched);
123}
124
125fe_trap::scoped_instance
126fe_trap::make_shared_scoped(std::optional<int> excepts) {
127 std::lock_guard<std::mutex> lock(fe_trap::global_state.mutex);
128 if (auto watched = fe_trap::global_state.observer.lock()) {
129 if (watched->is_unique()) {
130 throw std::runtime_error("Cannot create more than 1 instance of fe_trap");
131 }
132 if (watched->get_flags() != parse_excepts(excepts)) {
133 throw std::invalid_argument(
134 "Cannot mix different exceptions with fe_trap");
135 }
136 return fe_trap::scoped_instance(watched);
137 }
138 auto raw_ptr = new fe_trap(excepts, false);
139 auto watched = std::shared_ptr<fe_trap>(raw_ptr, deleter{});
140 fe_trap::global_state.observer = watched;
141 return fe_trap::scoped_instance(watched);
142}
143
144std::shared_ptr<fe_trap::scoped_pause> fe_trap::make_shared_pause_scoped() {
145 std::lock_guard<std::mutex> lock(fe_trap::global_state.mutex);
146 if (auto watched = fe_trap::global_state.observer.lock()) {
147 if (auto pause = watched->m_pause.lock()) {
148 return pause;
149 }
150 auto pause = std::make_shared<scoped_pause>(watched);
151 watched->m_pause = pause;
152 return pause;
153 }
154 return {};
155}
156
157#endif // ESPRESSO_FPE
Floating-point exception trap.
Definition fe_trap.hpp:88
void activate()
Manually activate the exception trap.
Definition fe_trap.cpp:42
void deactivate()
Manually deactivate the exception trap.
Definition fe_trap.cpp:72
static std::shared_ptr< scoped_pause > make_shared_pause_scoped()
Generate a shared handle to temporarily disable any currently active exception trap for the lifetime ...
Definition fe_trap.cpp:144
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:126
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:114