Page 1 of 2

new does not always throw a badalloc exception if memory cannot be allocated

Posted: Fri Jan 10, 2025 2:04 pm
by Richard
Hi,

I have asked Visual Micro

https://www.visualmicro.com/forums/YaBB ... 003983/0#0

why new does not always throw a badalloc exception if memory cannot be allocated. They recommended me to ask you.

Thanks
Richard

Re: new does not always throw a badalloc exception if memory cannot be allocated

Posted: Fri Jan 10, 2025 3:11 pm
by fpiSTM

Re: new does not always throw a badalloc exception if memory cannot be allocated

Posted: Fri Jan 10, 2025 3:31 pm
by Richard
Since the C++ standard requires new to throw an exception if memory cannot be allocated, you should provide a version compiled with -fexception.

Without, functions relying on this will not behave as expected. For example

Code: Select all

std::vector<int> v;
int too_much_push_backs()
{
  Serial.printf("Start too_much_push_backs() \n" );
  while (true) // leave at an exception
  {            //        from push_back    
    v.push_back(1);
    if (v.size() % 1000 == 0)
      Serial.printf("  v.size()=%d \n", v.size());
  }
}
will not behave standard conforming at a memory allocation error.

Thanks
Richard

Re: new does not always throw a badalloc exception if memory cannot be allocated

Posted: Fri Jan 10, 2025 3:47 pm
by ag123
there are still mcus with 4k memory around, there is no room for exceptions in those.
one can always edit the cflags in platform.txt and boards.txt locally if need be.
and malloc(), new etc are skeleton implementations
the stack can simply overwrite global variables with zero warning
let alone throw exceptions
microcontrollers are unlike cpu systems endowed with lots of memory and storage, with an os and specific hardware to manage that.

a 'well known' problem is use of complete desktop style libraries + exceptions will bloat the binary file quite a bit
https://miscsolutions.wordpress.com/201 ... -software/
for small mcus, they easily run out of flash or that your app / binary become 'too fat' to fit in flash while adding features / libraries.
if you are running on a chip with ample amount of memory and flash like 1 MB flash and like 512k sram and many megs of sdram and your app runs from sd ram. perhaps you could edit the flags in platform.txt locally to build a different binary.
add some good memory manager for malloc(), new etc and even run an os along with it.

Re: new does not always throw a badalloc exception if memory cannot be allocated

Posted: Fri Jan 10, 2025 5:31 pm
by fpiSTM
Richard wrote: Fri Jan 10, 2025 3:31 pm Since the C++ standard requires new to throw an exception if memory cannot be allocated, you should provide a version compiled with -fexception.
Well, no compiled version is provided, only source. You build on your side and you can override yourself the compilation options thanks *.local files options from Arduino, see the Arduino platform specification for this.

And @ag123 well explain the reasons for this.

Re: new does not always throw a badalloc exception if memory cannot be allocated

Posted: Fri Jan 10, 2025 6:09 pm
by Richard
Thanks fpiSTM,

can you please tell me where I can find these *.local files for the Arduino IDE?

Are these the same files for the "Arduino IDE for Visual Studio 2022" extension from Visual Micro? If not, where can I find them?

Can I set these flags in the Arduino IDE or in the Visual Micro Extension?

Richard

Re: new does not always throw a badalloc exception if memory cannot be allocated

Posted: Fri Jan 10, 2025 6:18 pm
by fpiSTM
You have to create it: https://arduino.github.io/arduino-cli/1 ... rmlocaltxt
Or modify the platform.txt.


I never used VS micro extension.

Re: new does not always throw a badalloc exception if memory cannot be allocated

Posted: Fri Jan 10, 2025 8:34 pm
by Richard
In the meantime I got the answer from the Visual Micro team

https://www.visualmicro.com/forums/YaBB ... 003983/0#2

Thanks a lot for all the answers. They were a great help.

Richard

Re: new does not always throw a badalloc exception if memory cannot be allocated

Posted: Tue Jan 14, 2025 5:55 pm
by Richard
Hi,

in my post from Fri Jan 10, 2025 9:34 pm I was too optimistic that I can solce the problem. Today I got

https://www.visualmicro.com/forums/YaBB ... 003983/0#0

from the VisualMicro team. That's why I have to come back to my posts and ask you again to change your build settings so that the -fexception switch in VisualMicro is passed to the compilation of the STM libraries.

I consider it a bug that after turning exception handling on, I get it only for a part of the code, with the final effect, that I don't get it at all for e.g. push_back.

There are many boards (e.g. the STM32 F303RE) with enough memory for exception handling. For such boards it should be possible to turn exception handling on. For an ESP32 with similiar memory it works this way.

A remark to your post by fpiSTM » Fri Jan 10, 2025 7:18 pm: After asking the VisualMicro team where I can set these flag (see the thread from above), I looked in boards.text and found that exception handling is already enabled:

compiler.cpp.flags={compiler.extra_flags} -c {compiler.optimization_flags} {compiler.warning_flags} -std={compiler.cpp.std} -ffunction-sections -fdata-sections -fno-threadsafe-statics --param max-inline-insns-single=500 -fno-rtti -fexceptions -fno-use-cxa-atexit -MMD {compiler.stm.extra_include}

Thanks
Richard

Re: new does not always throw a badalloc exception if memory cannot be allocated

Posted: Wed Jan 15, 2025 9:30 am
by fpiSTM
Richard wrote: Tue Jan 14, 2025 5:55 pm That's why I have to come back to my posts and ask you again to change your build settings so that the -fexception switch in VisualMicro is passed to the compilation of the STM libraries.
No, you can override it using platform.local.txt.
Richard wrote: Tue Jan 14, 2025 5:55 pm I consider it a bug that after turning exception handling on, I get it only for a part of the code, with the final effect, that I don't get it at all for e.g. push_back.
I never tested. It is common to simply disable it to save space. See below.
Richard wrote: Tue Jan 14, 2025 5:55 pmThere are many boards (e.g. the STM32 F303RE) with enough memory for exception handling. For such boards it should be possible to turn exception handling on. For an ESP32 with similiar memory it works this way.
Well, we feel free to contribute on this, it is a community project. Nothing prevent to enable it , choice is simply to save space using the Newlib nano by default (see below).
Richard wrote: Tue Jan 14, 2025 5:55 pm A remark to your post by fpiSTM » Fri Jan 10, 2025 7:18 pm: After asking the VisualMicro team where I can set these flag (see the thread from above), I looked in boards.text and found that exception handling is already enabled:

compiler.cpp.flags={compiler.extra_flags} -c {compiler.optimization_flags} {compiler.warning_flags} -std={compiler.cpp.std} -ffunction-sections -fdata-sections -fno-threadsafe-statics --param max-inline-insns-single=500 -fno-rtti -fexceptions -fno-use-cxa-atexit -MMD {compiler.stm.extra_include}
No, don't know how VSMicro handle build options but in the core, it is not enabled by default:

https://github.com/stm32duino/Arduino_C ... 71C1-L72C1

Code: Select all

compiler.cpp.flags={compiler.extra_flags} -c {compiler.optimization_flags} {compiler.warning_flags} -std={compiler.cpp.std} -ffunction-sections -fdata-sections -fno-threadsafe-statics --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -fno-use-cxa-atexit -MMD {compiler.stm.extra_include}
Looking at your code, this is normal new return 0 with or without exception enabled, new is defined here:
https://github.com/stm32duino/Arduino_C ... ew.cpp#L27

It simply calls malloc which return null if not able to allocate memory...

I've tested the exception with your code (using platform.local.txt to override default config) and called manually:

Code: Select all

 throw std::bad_alloc();
it works as expected but simply end in _exit() handler because even if exceptions are enable there is noexcept due to the use of the --specs=nano.specs by default. newlib-nano version of the standard library is built without exception support. So to be able to get the exception you have to use the Newlib statndard (full). It is possible to change this thanks the menu: C Runtime library.
Doing this allows to get the catch as you expect.

Code:

Code: Select all

void setup() {
  Serial.begin(9600);
}

void loop() {
  int* p = new int;
  delete p;
  int n = 1;
  int rep = 1;
  Serial.println("\nStart N_Exception_Basics::test_too_much_news(mit_Exceptions) ");
#ifdef __cpp_exceptions
  try {
#endif
    while (p != nullptr) {
      p = new int[n];
      delete[] p;
      n = n * 10;
      rep++;
      Serial.printf("n=%d p=%d \n", n, (int)p);
    }
#ifdef __cpp_exceptions
    throw std::bad_alloc();
  } catch (std::exception& e) {
    Serial.printf("Exception bei test_too_much_news n=%d what()=%s\n", n, e.what());
  } catch (...) {
    Serial.printf("... Exception bei test_too_much_news \n");
  }
  Serial.printf("nach catch ... Exception bei test_too_much_news \n");
#endif
}
Result:
Start N_Exception_Basics::test_too_much_news(mit_Exceptions)
n=10 p=536875600
n=100 p=536875600
n=1000 p=536875600
n=10000 p=536875600
n=100000 p=0
Exception bei test_too_much_news n=100000 what()=std::bad_alloc
nach catch ... Exception bei test_too_much_news
Here sum up of space used depending of the config:
-fexceptions and Newlib standard
Sketch uses 100120 bytes (76%) of program storage space. Maximum is 131072 bytes.
Global variables use 3584 bytes (10%) of dynamic memory, leaving 29184 bytes for local variables. Maximum is 32768 bytes.

-fexceptions and Newlib nano
Sketch uses 33220 bytes (25%) of program storage space. Maximum is 131072 bytes.
Global variables use 1488 bytes (4%) of dynamic memory, leaving 31280 bytes for local variables. Maximum is 32768 bytes.

-fno-exceptions and Newlib nano
Sketch uses 23864 bytes (18%) of program storage space. Maximum is 131072 bytes.
Global variables use 1444 bytes (4%) of dynamic memory, leaving 31324 bytes for local variables. Maximum is 32768 bytes.

-fno-exceptions and Newlib standard
Sketch uses 99248 bytes (75%) of program storage space. Maximum is 131072 bytes.
Global variables use 3584 bytes (10%) of dynamic memory, leaving 29184 bytes for local variables. Maximum is 32768 bytes.
To sum up if you want exception support using Arduino IDE or cli (other ide, tools,... are not supported here):
- override compiler.cpp.flags in a platform.local.txt to replace -fno-exceptions by -fexceptions
- use the Newlib standard.

And as stated before new return null if no space left but you can throw yourself the bad_alloc.