You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: doc/reference.rst
+65-31Lines changed: 65 additions & 31 deletions
Original file line number
Diff line number
Diff line change
@@ -219,34 +219,68 @@ using FPSTR would become...
219
219
C++
220
220
----
221
221
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
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
0 commit comments