PocketFFT (3rd/pocketfft, vendored 2018 version) cannot be compiled with -fno-exceptions, which PIP uses on all non-Windows/non-Android targets. This adds the proposed upstream patch adding a POCKETFFT_NO_EXCEPTIONS option (error paths abort with a diagnostic instead of throwing), plus the PR description for submission to mreineck/pocketfft. Verified: PIP builds with current upstream master + this patch and '-fno-exceptions -DPOCKETFFT_NO_EXCEPTIONS -DPOCKETFFT_NO_MULTITHREADING' and passes the FFT functional test suite. After the upstream PR is merged, bump the vendored header and drop the local workarounds.
99 lines
3.9 KiB
Markdown
99 lines
3.9 KiB
Markdown
# PR: Add `POCKETFFT_NO_EXCEPTIONS` option for builds without exception handling
|
||
|
||
Repo: https://github.com/mreineck/pocketfft
|
||
Branch base: `cpp` (upstream master)
|
||
|
||
---
|
||
|
||
## Motivation
|
||
|
||
pocketfft currently reports all errors via C++ exceptions (invalid arguments,
|
||
allocation failures, internal consistency checks). This makes the header
|
||
unusable in projects that are compiled without exception handling, e.g. with
|
||
GCC/Clang `-fno-exceptions` — a common setting in embedded,
|
||
safety-focused and performance-oriented code bases.
|
||
|
||
With `-fno-exceptions` the header fails to compile at every `throw` site:
|
||
|
||
```
|
||
pocketfft_hdronly.h: In function 'void* pocketfft::detail::aligned_alloc(...)':
|
||
error: exception handling disabled, use '-fexceptions' to enable
|
||
```
|
||
|
||
and additionally at the `catch` clauses in the thread pool (GCC rejects
|
||
`catch` under `-fno-exceptions` even with an empty body).
|
||
|
||
The only workaround today is to sed-patch the vendored header in every
|
||
downstream project. This PR adds an opt-in upstream option instead.
|
||
|
||
## What the PR adds
|
||
|
||
A new configuration macro `POCKETFFT_NO_EXCEPTIONS`, following the existing
|
||
`POCKETFFT_NO_VECTORS` / `POCKETFFT_NO_MULTITHREADING` naming convention:
|
||
|
||
```cpp
|
||
#define POCKETFFT_NO_EXCEPTIONS // optionally + POCKETFFT_NO_MULTITHREADING
|
||
#include "pocketfft_hdronly.h"
|
||
// build with -fno-exceptions as usual
|
||
```
|
||
|
||
Semantics: error conditions that would normally throw an exception instead
|
||
print a diagnostic to stderr and call `abort()`:
|
||
|
||
```
|
||
pocketfft: bad axis number
|
||
Aborted (core dumped)
|
||
```
|
||
|
||
## Design
|
||
|
||
- Every `throw std::...;` site (26 of them) is wrapped in a
|
||
`POCKETFFT_THROW(exception)` macro:
|
||
- **undefined (default):** expands to `throw exception` — behavior is
|
||
100% unchanged, exceptions are still catchable as before;
|
||
- **defined:** calls `POCKETFFT_NAMESPACE::detail::handle_fatal_error(e)`,
|
||
which prints `pocketfft: <what()>` to stderr and `std::abort()`.
|
||
- The two `try/catch` regions of the thread pool are `#if`-guarded, since
|
||
`catch` itself is a compile error under `-fno-exceptions`:
|
||
- `thread_pool::create_threads()`: without exception handling the worker
|
||
thread is created directly — a failing `std::thread` constructor already
|
||
results in `std::terminate()` per the standard, so nothing is lost;
|
||
- `threading::thread_map()`: the worker lambda simply calls `f()` without
|
||
the exception-capture plumbing.
|
||
- Why `abort()`: all error sites are unrecoverable (programmer errors such as
|
||
invalid size/stride/axis, or OOM). In a no-exception program there is no
|
||
meaningful recovery path, and abort-with-diagnostic is the customary
|
||
behavior. A user-configurable fatal handler could be a follow-up if
|
||
desired.
|
||
|
||
## Verification
|
||
|
||
`pocketfft_demo.cc` was built and run with GCC 15 and Clang (latest) in the
|
||
following configurations, with identical numerical output in all of them:
|
||
|
||
1. default (exceptions enabled) — unchanged behavior; additionally verified
|
||
that an out-of-range axis still throws a catchable `std::invalid_argument`;
|
||
2. `-DPOCKETFFT_NO_EXCEPTIONS` with exceptions enabled (mixed mode);
|
||
3. `-fno-exceptions -DPOCKETFFT_NO_EXCEPTIONS -DPOCKETFFT_NO_MULTITHREADING`;
|
||
4. `-fno-exceptions -DPOCKETFFT_NO_EXCEPTIONS` (thread pool compiled in).
|
||
|
||
Error-path check: with the macro defined (configurations 2–4), requesting a
|
||
transform with an out-of-range axis prints `pocketfft: bad axis number` and
|
||
aborts with SIGABRT.
|
||
|
||
Multithreaded check: with the macro defined and the thread pool enabled, a
|
||
1024-point transform with `nthreads=4` is bit-identical to the
|
||
single-threaded reference, across repeated pool runs.
|
||
|
||
## Files changed
|
||
|
||
- `pocketfft_hdronly.h` — the option (macro + `handle_fatal_error`, guarded
|
||
thread pool code);
|
||
- `README.md` — documentation entry in "Configuration options".
|
||
|
||
## Note for downstream users
|
||
|
||
This is what allows downstream projects that vendor the header and build with
|
||
`-fno-exceptions` (e.g. the PIP library) to drop their local header patches
|
||
and/or move to a current upstream release.
|