Skip to content

Make init() run before C++ static initializers #5592

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

eric-wieser
Copy link
Contributor

@eric-wieser eric-wieser commented Nov 12, 2016

Before this change, the contents of init() run after .init6 which is when static initializers run.

However, this is not desirable:

  • init() does not need any C++ classes to be active
  • C++ static initialization sometimes does require that the hardware be pre-configured!

This makes it possible to make calls like Serial.begin() inside constructors of global variables.

@Chris--A
Copy link
Contributor

This is a good idea, and essentially the same thing I do in my SmallSetup lib. However, for a permanent option I think this needs to be extended to the other cores (SAM, SAMD at least) to keep a level of consistency.

Otherwise we're making the requirements of the high-level API dependent on the low-level features. This will make the abstraction less.. abstract.

One option I have thought about is making the init() function re-entrant. So all classes that need it can call it from their constructor, but it'll only run once. This will make it easy to port between hardware and will solve the problem addressed in this issue.

Using a static bool to store the state of the init() function and by disabling interrupts, it should be safe to call where ever it may be required.

@bengtmartensson
Copy link

Can't you just rename the existing init() to something else, which you call yourself as you see fit, and let init() be empty?

@eric-wieser
Copy link
Contributor Author

eric-wieser commented Nov 12, 2016

@Chris--A: I agree, this has little value unless ported to all cores. I'll look at doing this once I get some more feedback on whether this is a good idea.

One option I have thought about is making the init() function re-entrant

That seems like a bit of a workaround, and requires more thinking on behalf of the user. Also, since static initialization order is undefined, that can put you in a position where a class that forgets to call init() will randomly work or not, depending on whether someone else got there first - this patch ensures that someone (the avr initialization) always gets there first.

@bengtmartensson: I'm not sure what you're asking for here. This PR aims to make it guaranteed that all user code runs after init().

@eric-wieser
Copy link
Contributor Author

@Chris--A:

However, for a permanent option I think this needs to be extended to the other cores

Am I right in thinking the only other core is hardware/arduino/sam/variants/arduino_due_x? My latest commit fixes it there too.

@mikaelpatel
Copy link

@eric-wieser When should interrupts be enabled? I suspect that that after the static initializations is a good and safe place??

@eric-wieser
Copy link
Contributor Author

eric-wieser commented Jan 2, 2017

@mikaelpatel: Very good point. It depends. If constructors hook up their own interrupt handlers with setInterrupt, then it doesn't matter, and enabling interrupts before gives more power to the constructors.

On the other hand, if any interrupts are directly hooked into the linker with assembler attributes (ISR(..._VECTOR)), then you're in for a bad time when your interrupt handler tries to use a static object that hasn't been constructed yet.

In any case, /hardware/arduino/avr/cores/arduino/HardwareSerial0.cpp uses an ISR(...) macro inside which it accesses a static object, so we've no real choice but to turn on interrupts after static constructors
What actually enables global interrupts in the Arduino code?

@mikaelpatel
Copy link

@eric-wieser I saw that the new init had sei and this got me thinking of possible ripple effects. Otherwise I like the idea of forcing init before static constructors and will be adding this to Cosa where I have looked for a good solution; https://github.com/mikaelpatel/Cosa

@eric-wieser
Copy link
Contributor Author

the new init had sei

Oh damn. That's gotta move.

@PaulStoffregen
Copy link
Contributor

Might be worth mentioning ARM Cortex-M's PRIMASK defaults to clear at boot time, which allows interrupts by default. This is different from AVR, where the global interrupt enable GIE bit defaults to zero, which inhibits all interrupts by default, until it's set by software.

On AVR, the GIE bit serves at least 3 functions:

  1. enabling all interrupts
  2. temporary masking all interrupts for critical sections in code
  3. blocking other interrupts while executing interrupt code

On ARM Cortex-M chips, generally #1 is done by a set of registers within the interrupt controller, where each bit corresponds to 1 of the interrupts. PRIMASK is meant for #2. The Cortex-M interrupt controller implements a nested priority scheme in hardware, which is separate from these other two register mechanisms.

Many people who are very familiar with the GIE bit in AVR often lump these 3 very distinct usages together, since the simple hardware in AVR (and PIC) chips provides just 1 global bit. When designing for ARM Cortex-M chips, it's a common mistake to think of PRIMASK this way. It's really only meant for case #2, and by default it allows as interrupts from the moment the ARM core resets.

@facchinm facchinm added Architecture: AVR Applies only to the AVR microcontrollers (Uno, etc.) Architecture: SAM Applies only to the SAM microcontrollers (Due) labels Jan 20, 2017
@facchinm
Copy link
Member

Partially moved to arduino/ArduinoCore-sam#4, the patch will need to be rebased once SAM core gets detached

@facchinm
Copy link
Member

I'd love to create a build with this PR but the SAM part must be removed so solve the conflict. @eric-wieser could you rebase it on master? Thanks

Before this change, the contents of `init()` run after `.init6`, which is when static initializers run.

However, this is not desirable:
* init() does not need any C++ classes to be active
* C++ static initialization sometimes _does_ require that the hardware be pre-configured!

This makes it possible to make calls like `Serial.begin()` inside constructors of global variables.
@eric-wieser
Copy link
Contributor Author

Done

@facchinm
Copy link
Member

@ArduinoBot build this please

@cmaglie cmaglie modified the milestones: Release 1.8.3, Next Jul 18, 2017
@eric-wieser
Copy link
Contributor Author

Is there anything that can be done to move this along?

@facchinm
Copy link
Member

Closing here; if someone wants to adopt this PR, please reopen on ArduinoCore-avr repo suing this link https://github.com/arduino/ArduinoCore-avr/compare/pr_5592?expand=1

@facchinm facchinm closed this Sep 19, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Architecture: AVR Applies only to the AVR microcontrollers (Uno, etc.) Architecture: SAM Applies only to the SAM microcontrollers (Due)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants