Skip to content

Commit 7daaec9

Browse files
committed
per review
1 parent e48861b commit 7daaec9

File tree

1 file changed

+65
-31
lines changed

1 file changed

+65
-31
lines changed

doc/reference.rst

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -219,34 +219,68 @@ using FPSTR would become...
219219
C++
220220
----
221221

222-
About C++ `new` operator and return value
223-
224-
On heap shortage (memory full), `new` operator in C++ standards:
225-
- is supposed to throw an exception
226-
- or call abort() when exceptions are disabled because `new` is supposed to never return `nullptr` (`NULL`).
227-
228-
Usually, exceptions are by default disabled in Arduino worlds.
229-
Historically in Arduino environments, `new` is overloaded to simply return the equivalent `malloc()` which in turn can return `nullptr`, which is not standard for `new`. It is considered as acceptable.
230-
But it has a hidden and very bad side effect: the class and member constructors are always called, even when memory is full (`this`=`nullptr`). So any memory shortage, when using `new`, will lead to bad crashes sooner or later, sometimes unexplainable, generally due to memory corruption even when the returned value is checked and managed.
231-
232-
In this core after version 2.5.2, we will stick to C++ standards: `new` will call `abort()` and will "cleanly" crash when exceptions are disabled and memory allocation cannot be honored.
233-
234-
However, a new optional global allocator is introduced with a different semantic. It is similar to `new` but will return `nullptr` without side effects, as expected in arduino world. Syntax is a bit different:
235-
236-
.. code:: cpp
237-
238-
SomeClass* sc = new SomeClass(arg1, arg2, ...);
239-
// sc is always valid and not nullptr, no check necessary
240-
// abort() may have been called (crash dump, reboot)
241-
// or an exception thrown when available
242-
243-
becomes:
244-
245-
.. code:: cpp
246-
247-
SomeClass* sc = new0<SomeClass>(arg1, arg2, ...);
248-
// abort() is never called, an exception is not thrown even if they are enabled
249-
if (sc == nullptr) // do something
250-
else // use sc
251-
252-
History: `#6269 <https://github.com/esp8266/Arduino/issues/6269>`__ `#6309 <https://github.com/esp8266/Arduino/pull/6309>`__ `#6312 <https://github.com/esp8266/Arduino/pull/6312>`__
222+
- About C++ `new` operator and return value
223+
224+
The C++ standard says the following about the `new` operator behavior when encountering heap shortage (memory full):
225+
- has to throw a std::bad_alloc C++ exception
226+
- throw an unhandled exception, which means calling abort()
227+
228+
There are several reasons for the first point above, among which are:
229+
- guarantee that the return of new is never a nullptr
230+
- guarantee full construction of the top level object plus all member subobjects
231+
- guarantee that any subobjects partially constructed get destroyed, and in the correct order, if oom is encountered midway through construction
232+
233+
When C++ exceptions are disabled, or when using new(nothrow), the above guarantees can't be upheld, so the second point above is the only viable solution.
234+
235+
Historically in Arduino environments, `new` is overloaded to simply return the equivalent `malloc()` which in turn can return `nullptr`. In other cores, and up to our core version 2.5.2, that is considered as acceptable.
236+
237+
However, this behavior is not C++ standard, and there is good reason for that: there are hidden and very bad side effects. The class and member constructors are always called, even when memory is full (`this`=`nullptr`). In addition, the memory allocation for the top object could succeed, but allocation required for some member object could fail, leaving construction in an undefined state. So the historical behavior of Ardudino's `new`, when faced with insufficient memory, will lead to bad crashes sooner or later, sometimes unexplainable, generally due to memory corruption even when the returned value is checked and managed.
238+
239+
As of core 2.6.0, we are sticking to the C++ standard. There are two clear cases when `new` encounters oom:
240+
- C++ exceptions are disabled (default build): `new` causes an exception, which in turn calls `abort()` and will "cleanly" crash, because there is no way to honor memory allocation or to recover gracefully.
241+
- C++ exceptions are enabled (menu option): `new` throws a std::bad_alloc C++ exception, which can be caught and handled gracefully. This assures correct behavior, including handling of all subobjects, which guarantees stability.
242+
243+
To allow previous behavior, a new optional global allocator is introduced with a different semantic. It is similar to `new` but will return `nullptr` without side effects (if `std::new` is not used in constructors), as expected in arduino world. Syntax is slightly different:
244+
245+
Syntax is slightly different, the following shows the different usages:
246+
247+
C++ standard behavior (as of 2.6.0):
248+
249+
.. code:: cpp
250+
251+
SomeClass* sc = new SomeClass(arg1, arg2, ...);
252+
// sc is always valid and not nullptr, no check necessary
253+
// abort() gets called (crash dump, reboot) if oom,
254+
// or a C++ std::bad_alloc exception is thrown when available
255+
256+
Old behavior (until 2.5.2):
257+
258+
.. code:: cpp
259+
260+
SomeClass* sc = new0<SomeClass>(arg1, arg2, ...);
261+
// abort() is never called, an exception is not thrown even if they are enabled
262+
if (sc == nullptr)
263+
{
264+
// failed allocation
265+
}
266+
else
267+
{
268+
// use sc
269+
}
270+
271+
Alternate behavior (as of 2.6.0):
272+
273+
.. code:: cpp
274+
275+
SomeClass* sc = new0<SomeClass>(arg1, arg2, ...);
276+
// abort() is never called, an exception is not thrown even if they are enabled
277+
if (sc == nullptr)
278+
{
279+
// failed allocation, handle here
280+
}
281+
else
282+
{
283+
// use sc
284+
}
285+
286+
History: `#6269 <https://github.com/esp8266/Arduino/issues/6269>`__ `#6309 <https://github.com/esp8266/Arduino/pull/6309>`__ `#6312 <https://github.com/esp8266/Arduino/pull/6312>`__

0 commit comments

Comments
 (0)