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 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 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
42fe_trap::fe_trap(std::optional<int> excepts, bool unique) {
43#if defined(ESPRESSO_FPE_USING_GLIBC_X86_64)
44 {
45 m_flags = parse_excepts(excepts);
46 [[maybe_unused]] auto const status = feenableexcept(m_flags);
47 // note: status should be 0 since we use the singleton pattern
48 assert(status == 0);
49 }
50#elif defined(ESPRESSO_FPE_USING_APPLE_ARM_64)
51 {
52 using fpcr_t = decltype(std::fenv_t::__fpcr);
53 m_flags = parse_excepts(excepts);
54 std::fenv_t env;
55 {
56 [[maybe_unused]] auto const status = std::fegetenv(&env);
57 assert(status == 0u);
58 }
59 env.__fpcr |= static_cast<fpcr_t>(m_flags);
60 {
61 [[maybe_unused]] auto const status = std::fesetenv(&env);
62 assert(status == 0u);
63 }
64 }
65#else
66#error "FE not supported"
67#endif
68 m_unique = unique;
69}
70
71fe_trap::~fe_trap() {
72#if defined(ESPRESSO_FPE_USING_GLIBC_X86_64)
73 {
74 [[maybe_unused]] auto const status = fedisableexcept(m_flags);
75 // note: status can become 0 in a signal handler that calls @c siglongjmp()
76 assert(status == 0 or status == m_flags);
77 }
78#elif defined(ESPRESSO_FPE_USING_APPLE_ARM_64)
79 {
80 using fpcr_t = decltype(std::fenv_t::__fpcr);
81 std::fenv_t env;
82 {
83 [[maybe_unused]] auto const status = std::fegetenv(&env);
84 assert(status == 0u);
85 }
86 assert((env.__fpcr & static_cast<fpcr_t>(m_flags)) ==
87 static_cast<fpcr_t>(m_flags));
88 env.__fpcr &= static_cast<fpcr_t>(~m_flags);
89 {
90 [[maybe_unused]] auto const status = std::fesetenv(&env);
91 assert(status == 0u);
92 }
93 }
94#else
95#error "FE not supported"
96#endif
97}
98
99int fe_trap::parse_excepts(std::optional<int> excepts) {
100 auto const fallback = FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW;
101 int retval = excepts ? *excepts : fallback;
102#if defined(ESPRESSO_FPE_USING_APPLE_ARM_64)
103 retval <<= 8;
104#endif
105 return retval;
106}
107
108fe_trap::scoped_instance
109fe_trap::make_unique_scoped(std::optional<int> excepts) {
110 std::lock_guard<std::mutex> lock(fe_trap::global_state.mutex);
111 if (fe_trap::global_state.observer.lock()) {
112 throw std::runtime_error("Cannot create more than 1 instance of fe_trap");
113 }
114 auto raw_ptr = new fe_trap(excepts, true);
115 auto watched = std::shared_ptr<fe_trap>(raw_ptr, deleter{});
116 fe_trap::global_state.observer = watched;
117 return fe_trap::scoped_instance(watched);
118}
119
120fe_trap::scoped_instance
121fe_trap::make_shared_scoped(std::optional<int> excepts) {
122 std::lock_guard<std::mutex> lock(fe_trap::global_state.mutex);
123 if (auto watched = fe_trap::global_state.observer.lock()) {
124 if (watched->is_unique()) {
125 throw std::runtime_error("Cannot create more than 1 instance of fe_trap");
126 }
127 if (watched->get_flags() != parse_excepts(excepts)) {
128 throw std::invalid_argument(
129 "Cannot mix different exceptions with fe_trap");
130 }
131 return fe_trap::scoped_instance(watched);
132 }
133 auto raw_ptr = new fe_trap(excepts, false);
134 auto watched = std::shared_ptr<fe_trap>(raw_ptr, deleter{});
135 fe_trap::global_state.observer = watched;
136 return fe_trap::scoped_instance(watched);
137}
138
139#endif // FPE
Floating-point exception trap.
Definition fe_trap.hpp:87
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
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.