From 1a885ce890219213ac24c00d5aededa88c122fe1 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 17 Sep 2020 17:59:08 +0200 Subject: Optionally let new terminate on allocation failure This is currently disabled, keeping the old behavior of returning NULL on failure, but should probably be enabled in the future as code that does want to do a null check has had a chance to switch to the more portable nothrow versions. When enabled, allocation failure calls the weak `std::terminate()`, which calls `abort()` by default, but can be replaced by user code to do more specific handling. To enable this, a macro must be defined (in new.cpp or on the compiler commandline). This fixes part of #287. --- cores/arduino/new.cpp | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/cores/arduino/new.cpp b/cores/arduino/new.cpp index 1683594..19d80b6 100644 --- a/cores/arduino/new.cpp +++ b/cores/arduino/new.cpp @@ -18,26 +18,61 @@ #include "new.h" +// The C++ spec dicates that allocation failure should cause the +// (non-nothrow version of the) operator new to throw an exception. +// Since we expect to have exceptions disabled, it would be more +// appropriate (and probably standards-compliant) to terminate instead. +// Historically failure causes null to be returned, but this define +// allows switching to more robust terminating behaviour (that might +// become the default at some point in the future). Note that any code +// that wants null to be returned can (and should) use the nothrow +// versions of the new statement anyway and is unaffected by this. +// #define NEW_TERMINATES_ON_FAILURE + namespace std { + // Defined in abi.cpp + void terminate(); + const nothrow_t nothrow; } -void * operator new(size_t size) { +static void * new_helper(size_t size) { // Even zero-sized allocations should return a unique pointer, but // malloc does not guarantee this if (size == 0) size = 1; return malloc(size); } + +void * operator new(size_t size) { + void *res = new_helper(size); +#if defined(NEW_TERMINATES_ON_FAILURE) + if (!res) + std::terminate(); +#endif + return res; +} void * operator new[](size_t size) { return operator new(size); } void * operator new(size_t size, const std::nothrow_t tag) noexcept { +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new as standard suggests, so call + // new_helper directly then + return new_helper(size); +#else return operator new(size); +#endif } void * operator new[](size_t size, const std::nothrow_t& tag) noexcept { +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new[] as standard suggests, so call + // malloc directly then + return new_helper(size); +#else return operator new[](size); +#endif } void * operator new(size_t size, void *place) noexcept { -- cgit v1.2.3-18-g5258