From 27a7fb65e63dc0a6e4d909c8946af6a41bb719bf Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Fri, 6 Nov 2026 16:30:05 +0300 Subject: [PATCH 1/6] FunctionalInterrupt - limit shared code w/ digital wiring private struct handling limited to one compilation unit real funcs get pulled whenever attachInterrupt requests std::function overload weakly linked stubs are used by default --- cores/esp8266/FunctionalInterrupt.cpp | 103 ++++++++++++------ cores/esp8266/FunctionalInterrupt.h | 40 +++---- cores/esp8266/core_esp8266_wiring_digital.cpp | 55 +++++----- 3 files changed, 114 insertions(+), 84 deletions(-) diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp index f468595787..98f3c2fef9 100644 --- a/cores/esp8266/FunctionalInterrupt.cpp +++ b/cores/esp8266/FunctionalInterrupt.cpp @@ -1,65 +1,104 @@ -#include -#include +// core_esp8266_wiring_digital helper for std::function<...> dispatch + #include "Arduino.h" +#include "Schedule.h" + +#include "FunctionalInterrupt.h" -// Duplicate typedefs from core_esp8266_wiring_digital_c -typedef void (*voidFuncPtr)(void); -typedef void (*voidFuncPtrArg)(void*); +extern "C" { +#include "c_types.h" +#include "ets_sys.h" +} -// Helper functions for Functional interrupt routines -extern "C" void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtr userFunc, void*fp, int mode, bool functional); +namespace esp8266::FunctionalInterrupt { +namespace { +struct ArgStructure { + InterruptInfo* interruptInfo; + FunctionInfo* functionInfo; +}; + +} -void IRAM_ATTR interruptFunctional(void* arg) +void IRAM_ATTR updateArg(void* varg, uint8_t i, uint8_t value, uint32_t micro) { + ArgStructure* arg = reinterpret_cast(varg); + if (arg && arg->interruptInfo) + { + arg->interruptInfo->pin = i; + arg->interruptInfo->value = value; + arg->interruptInfo->micro = micro; + } +} + +void IRAM_ATTR dispatchFunction(void* varg) { - ArgStructure* localArg = (ArgStructure*)arg; - if (localArg->functionInfo->reqScheduledFunction) - { - schedule_function(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo)))); - } - if (localArg->functionInfo->reqFunction) - { - localArg->functionInfo->reqFunction(); - } + ArgStructure* arg = reinterpret_cast(varg); + arg->functionInfo->reqFunction(); } -extern "C" +void IRAM_ATTR dispatchScheduled(void* varg) { - void cleanupFunctional(void* arg) - { - ArgStructure* localArg = (ArgStructure*)arg; - delete (FunctionInfo*)localArg->functionInfo; - delete (InterruptInfo*)localArg->interruptInfo; - delete localArg; - } + ArgStructure* arg = reinterpret_cast(varg); + schedule_function(std::bind(arg->functionInfo->reqScheduledFunction, InterruptInfo(*(arg->interruptInfo)))); } -void attachInterrupt(uint8_t pin, std::function intRoutine, int mode) +// see core_esp8266_wiring_digital.cpp, these are referenced there +void cleanup(void* varg) { - // use the local interrupt routine which takes the ArgStructure as argument + ArgStructure* arg = reinterpret_cast(varg); + delete (FunctionInfo*)arg->functionInfo; + delete (InterruptInfo*)arg->interruptInfo; + delete arg; +} + +// these would be used by the the local interrupt routine which takes the ArgStructure as argument +void prepareFunction(void** funcPtr, void** argPtr, Function intRoutine) +{ InterruptInfo* ii = nullptr; FunctionInfo* fi = new FunctionInfo; - fi->reqFunction = intRoutine; + fi->reqFunction = std::move(intRoutine); ArgStructure* as = new ArgStructure; as->interruptInfo = ii; as->functionInfo = fi; - __attachInterruptFunctionalArg(pin, (voidFuncPtr)interruptFunctional, as, mode, true); + (*funcPtr) = (void*)&dispatchFunction; + (*argPtr) = as; } -void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode) +void prepareScheduledFunction(void** funcPtr, void** argPtr, ScheduledFunction intRoutine) { InterruptInfo* ii = new InterruptInfo; FunctionInfo* fi = new FunctionInfo; - fi->reqScheduledFunction = scheduledIntRoutine; + fi->reqScheduledFunction = std::move(intRoutine); ArgStructure* as = new ArgStructure; as->interruptInfo = ii; as->functionInfo = fi; - __attachInterruptFunctionalArg(pin, (voidFuncPtr)interruptFunctional, as, mode, true); + (*funcPtr) = (void*)&dispatchScheduled; + (*argPtr) = as; +} + +} // namespace esp8266::FunctionalInterrupt + +extern "C" void __attachInterruptFunctionalArg(uint8_t, void (*)(void*), void*, int, bool functional); + +void attachInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::Function intRoutine, int mode) +{ + void* func; + void* arg; + esp8266::FunctionalInterrupt::prepareFunction(&func, &arg, std::move(intRoutine)); + __attachInterruptFunctionalArg(pin, reinterpret_cast(func), arg, mode, true); +} + +void attachScheduledInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::ScheduledFunction intRoutine, int mode) +{ + void* func; + void* arg; + esp8266::FunctionalInterrupt::prepareScheduledFunction(&func, &arg, std::move(intRoutine)); + __attachInterruptFunctionalArg(pin, reinterpret_cast(func), arg, mode, true); } diff --git a/cores/esp8266/FunctionalInterrupt.h b/cores/esp8266/FunctionalInterrupt.h index 968793e499..0c136242c2 100644 --- a/cores/esp8266/FunctionalInterrupt.h +++ b/cores/esp8266/FunctionalInterrupt.h @@ -1,35 +1,29 @@ -#ifndef FUNCTIONALINTERRUPT_H -#define FUNCTIONALINTERRUPT_H +// core_esp8266_wiring_digital helper for std::function<...> dispatch -#include -#include +#pragma once + +#include +#include #include -extern "C" { -#include "c_types.h" -#include "ets_sys.h" -} +namespace esp8266::FunctionalInterrupt { -// Structures for communication +using Function = std::function; struct InterruptInfo { - uint8_t pin = 0; - uint8_t value = 0; - uint32_t micro = 0; + uint8_t pin; + uint8_t value; + uint32_t micro; }; -struct FunctionInfo { - std::function reqFunction = nullptr; - std::function reqScheduledFunction = nullptr; -}; +using ScheduledFunction = std::function; -struct ArgStructure { - InterruptInfo* interruptInfo = nullptr; - FunctionInfo* functionInfo = nullptr; +struct FunctionInfo { + Function reqFunction; + ScheduledFunction reqScheduledFunction; }; -void attachInterrupt(uint8_t pin, std::function intRoutine, int mode); -void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode); - +} // namespace esp8266::FunctionalInterrupt -#endif //INTERRUPTS_H +void attachInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::Function, int mode); +void attachScheduledInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::ScheduledFunction, int mode); diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index 6b42c3ee36..b0450f4497 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -28,6 +28,20 @@ #include "core_esp8266_waveform.h" #include "interrupts.h" +namespace esp8266::FunctionalInterrupt { + +// in-place stubs, in case functional interrupts arent used + +void updateArg(void*, uint8_t, uint8_t, uint32_t) __attribute__((weak)); +void IRAM_ATTR updateArg(void*, uint8_t, uint8_t, uint32_t) { +} + +void cleanup(void*) __attribute__((weak)); +void cleanup(void*) { +} + +} // namespace esp8266::FunctionalInterrupt + extern "C" { volatile uint32_t* const esp8266_gpioToFn[16] PROGMEM = { &GPF0, &GPF1, &GPF2, &GPF3, &GPF4, &GPF5, &GPF6, &GPF7, &GPF8, &GPF9, &GPF10, &GPF11, &GPF12, &GPF13, &GPF14, &GPF15 }; @@ -106,27 +120,15 @@ extern int IRAM_ATTR __digitalRead(uint8_t pin) { GPIO INTERRUPTS */ -typedef void (*voidFuncPtr)(void); -typedef void (*voidFuncPtrArg)(void*); +using voidFuncPtr = void(*)(); +using voidFuncPtrArg = void(*)(void*); -typedef struct { +struct interrupt_handler_t { uint8_t mode; voidFuncPtr fn; - void * arg; + void* arg; bool functional; -} interrupt_handler_t; - -//duplicate from functionalInterrupt.h keep in sync -typedef struct InterruptInfo { - uint8_t pin; - uint8_t value; - uint32_t micro; -} InterruptInfo; - -typedef struct { - InterruptInfo* interruptInfo; - void* functionInfo; -} ArgStructure; +}; static interrupt_handler_t interrupt_handlers[16] = { {0, 0, 0, 0}, }; static uint32_t interrupt_reg = 0; @@ -154,13 +156,8 @@ void IRAM_ATTR interrupt_handler(void *arg, void *frame) esp8266::InterruptLock irqLock; // stop other interrupts if (handler->functional) { - ArgStructure* localArg = (ArgStructure*)handler->arg; - if (localArg && localArg->interruptInfo) - { - localArg->interruptInfo->pin = i; - localArg->interruptInfo->value = __digitalRead(i); - localArg->interruptInfo->micro = micros(); - } + esp8266::FunctionalInterrupt::updateArg( + handler->arg, i, __digitalRead(i), micros()); } if (handler->arg) { @@ -175,8 +172,6 @@ void IRAM_ATTR interrupt_handler(void *arg, void *frame) ETS_GPIO_INTR_ENABLE(); } -extern void cleanupFunctional(void* arg); - static void set_interrupt_handlers(uint8_t pin, voidFuncPtr userFunc, void* arg, uint8_t mode, bool functional) { interrupt_handler_t* handler = &interrupt_handlers[pin]; @@ -184,7 +179,7 @@ static void set_interrupt_handlers(uint8_t pin, voidFuncPtr userFunc, void* arg, handler->fn = userFunc; if (handler->functional && handler->arg) // Clean when new attach without detach { - cleanupFunctional(handler->arg); + esp8266::FunctionalInterrupt::cleanup(handler->arg); } handler->arg = arg; handler->functional = functional; @@ -225,7 +220,7 @@ extern void IRAM_ATTR __detachInterrupt(uint8_t pin) { GPC(pin) &= ~(0xF << GPCI);//INT mode disabled GPIEC = (1 << pin); //Clear Interrupt for this pin interrupt_reg &= ~(1 << pin); - set_interrupt_handlers(pin, nullptr, nullptr, 0, false); + set_interrupt_handlers(pin, nullptr, nullptr, 0, false); if (interrupt_reg) { ETS_GPIO_INTR_ENABLE(); @@ -256,10 +251,12 @@ extern void initPins() { extern void resetPins() __attribute__ ((weak, alias("__resetPins"))); extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pinMode"))); + extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite"))); extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead"), nothrow)); + extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt"))); extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void* arg, int mode) __attribute__((weak, alias("__attachInterruptArg"))); extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt"))); -}; +} // extern "C" From 57aebe48737e06325bc35bb355427301020c914a Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Fri, 6 Nov 2026 21:08:10 +0300 Subject: [PATCH 2/6] SPISlave - pointers instead of std::function objects add a note about unsafe execution from isr context --- .../examples/SPISlave_Test/SPISlave_Test.ino | 3 ++ libraries/SPISlave/src/SPISlave.cpp | 4 +++ libraries/SPISlave/src/SPISlave.h | 34 ++++++++----------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino b/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino index 3b8bf0e01c..543e759007 100644 --- a/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino +++ b/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino @@ -13,6 +13,9 @@ the ESP8266 WILL FAIL to boot! See SPISlave_SafeMaster example for possible workaround + Warning! sketch unsafely executes SPISlave callbacks in ISR context + For real application, make sure to place these functions into IRAM + */ #include "SPISlave.h" diff --git a/libraries/SPISlave/src/SPISlave.cpp b/libraries/SPISlave/src/SPISlave.cpp index 3d58a86721..66609c4dae 100644 --- a/libraries/SPISlave/src/SPISlave.cpp +++ b/libraries/SPISlave/src/SPISlave.cpp @@ -90,6 +90,10 @@ void SPISlaveClass::setData(uint8_t * data, size_t len) } hspi_slave_setData(data, len); } +void SPISlaveClass::setData(const char* data) +{ + setData((uint8_t *)data, strlen(data)); +} void SPISlaveClass::setStatus(uint32_t status) { hspi_slave_setStatus(status); diff --git a/libraries/SPISlave/src/SPISlave.h b/libraries/SPISlave/src/SPISlave.h index 86b30875f2..3feb3e7870 100644 --- a/libraries/SPISlave/src/SPISlave.h +++ b/libraries/SPISlave/src/SPISlave.h @@ -21,20 +21,21 @@ #ifndef _SPISLAVE_H_INCLUDED #define _SPISLAVE_H_INCLUDED -#include "Arduino.h" -#include +#include +#include +#include -typedef std::function SpiSlaveDataHandler; -typedef std::function SpiSlaveStatusHandler; -typedef std::function SpiSlaveSentHandler; +using SpiSlaveDataHandler = void(*)(uint8_t *, size_t ); +using SpiSlaveStatusHandler = void(*)(uint32_t status); +using SpiSlaveSentHandler = void(*)(); class SPISlaveClass { protected: - SpiSlaveDataHandler _data_cb; - SpiSlaveStatusHandler _status_cb; - SpiSlaveSentHandler _data_sent_cb; - SpiSlaveSentHandler _status_sent_cb; + SpiSlaveDataHandler _data_cb = nullptr; + SpiSlaveStatusHandler _status_cb = nullptr; + SpiSlaveSentHandler _data_sent_cb = nullptr; + SpiSlaveSentHandler _status_sent_cb = nullptr; void _data_rx(uint8_t * data, uint8_t len); void _status_rx(uint32_t data); void _data_tx(void); @@ -44,21 +45,14 @@ class SPISlaveClass static void _s_data_tx(void *arg); static void _s_status_tx(void *arg); public: - SPISlaveClass() - : _data_cb(NULL) - , _status_cb(NULL) - , _data_sent_cb(NULL) - , _status_sent_cb(NULL) - {} - ~SPISlaveClass() {} + SPISlaveClass() = default; + ~SPISlaveClass() = default; + void begin(); void begin(uint8_t statusLength); void end(); void setData(uint8_t * data, size_t len); - void setData(const char * data) - { - setData((uint8_t *)data, strlen(data)); - } + void setData(const char * data); void setStatus(uint32_t status); void onData(SpiSlaveDataHandler cb); void onDataSent(SpiSlaveSentHandler cb); From 4f0c8a15cc3db2172885e9db9a8e81fb8798481e Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Fri, 6 Nov 2026 21:09:32 +0300 Subject: [PATCH 3/6] FunctionalInterrupt - explicit function type tagging ref. https://github.com/esp8266/Arduino/pull/5922 still utilize section split, but put both operator and invoker latter being was omitted for some reason in the original pr additionally, resolve ambigous overload of base attachInterrupt in case functor can be converted into a pointer but cannot decide between std::function and void(*)(void) --- cores/esp8266/FunctionalInterrupt.cpp | 2 +- cores/esp8266/FunctionalInterrupt.h | 12 +++++++++++- tools/sdk/ld/eagle.app.v6.common.ld.h | 5 +++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp index 98f3c2fef9..44331d2bee 100644 --- a/cores/esp8266/FunctionalInterrupt.cpp +++ b/cores/esp8266/FunctionalInterrupt.cpp @@ -33,7 +33,7 @@ void IRAM_ATTR updateArg(void* varg, uint8_t i, uint8_t value, uint32_t micro) { void IRAM_ATTR dispatchFunction(void* varg) { ArgStructure* arg = reinterpret_cast(varg); - arg->functionInfo->reqFunction(); + arg->functionInfo->reqFunction(Tag{}); } void IRAM_ATTR dispatchScheduled(void* varg) diff --git a/cores/esp8266/FunctionalInterrupt.h b/cores/esp8266/FunctionalInterrupt.h index 0c136242c2..ad6bb73472 100644 --- a/cores/esp8266/FunctionalInterrupt.h +++ b/cores/esp8266/FunctionalInterrupt.h @@ -8,7 +8,12 @@ namespace esp8266::FunctionalInterrupt { -using Function = std::function; +using PlainFunction = std::function; + +struct Tag { +}; + +using Function = std::function; struct InterruptInfo { uint8_t pin; @@ -27,3 +32,8 @@ struct FunctionInfo { void attachInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::Function, int mode); void attachScheduledInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::ScheduledFunction, int mode); + +[[deprecated("3.2.x: use function type w/ ::Tag as argument")]] +inline void attachInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::PlainFunction func, int mode) { + attachInterrupt(pin, [func = std::move(func)](auto) { func(); }, mode); +} diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.h b/tools/sdk/ld/eagle.app.v6.common.ld.h index e6b415c50b..e959f0c4f1 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -140,8 +140,9 @@ SECTIONS *(.text.gdbstub* .text.gdb_init) /* Any GDB hooks */ - /* all functional callers are placed in IRAM (including SPI/IRQ callbacks/etc) here */ - *(.text._ZNKSt8functionIF*EE*) /* std::function::operator()() const */ + /* functional interrupt placed in IRAM */ + *(.text._ZNKSt8functionIFvN7esp826619FunctionalInterrupt3TagEE*) + *(.text._ZNSt17_Function_handlerIFvN7esp826619FunctionalInterrupt3TagEE*_M_invoke*) #ifdef FP_IN_IRAM *libgcc.a:*f2.o(.literal .text) From ef7fb1116718e1b983e5ef600dab7fb7602c77f1 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Fri, 6 Nov 2026 22:05:58 +0300 Subject: [PATCH 4/6] FunctionalInterrupt - missing delete and dtor calls for detachInterrupt --- cores/esp8266/FunctionalInterrupt.cpp | 2 +- cores/esp8266/core_esp8266_wiring_digital.cpp | 4 ++-- tools/sdk/ld/eagle.app.v6.common.ld.h | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp index 44331d2bee..16f8fef604 100644 --- a/cores/esp8266/FunctionalInterrupt.cpp +++ b/cores/esp8266/FunctionalInterrupt.cpp @@ -43,7 +43,7 @@ void IRAM_ATTR dispatchScheduled(void* varg) } // see core_esp8266_wiring_digital.cpp, these are referenced there -void cleanup(void* varg) +void IRAM_ATTR cleanup(void* varg) { ArgStructure* arg = reinterpret_cast(varg); delete (FunctionInfo*)arg->functionInfo; diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index b0450f4497..b13d34c2a7 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -37,7 +37,7 @@ void IRAM_ATTR updateArg(void*, uint8_t, uint8_t, uint32_t) { } void cleanup(void*) __attribute__((weak)); -void cleanup(void*) { +void IRAM_ATTR cleanup(void*) { } } // namespace esp8266::FunctionalInterrupt @@ -172,7 +172,7 @@ void IRAM_ATTR interrupt_handler(void *arg, void *frame) ETS_GPIO_INTR_ENABLE(); } -static void set_interrupt_handlers(uint8_t pin, voidFuncPtr userFunc, void* arg, uint8_t mode, bool functional) +static void IRAM_ATTR set_interrupt_handlers(uint8_t pin, voidFuncPtr userFunc, void* arg, uint8_t mode, bool functional) { interrupt_handler_t* handler = &interrupt_handlers[pin]; handler->mode = mode; diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.h b/tools/sdk/ld/eagle.app.v6.common.ld.h index e959f0c4f1..450b7f102c 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -143,6 +143,7 @@ SECTIONS /* functional interrupt placed in IRAM */ *(.text._ZNKSt8functionIFvN7esp826619FunctionalInterrupt3TagEE*) *(.text._ZNSt17_Function_handlerIFvN7esp826619FunctionalInterrupt3TagEE*_M_invoke*) + *(.text._ZNSt17_Function_handlerIFvN7esp826619FunctionalInterrupt3TagEE*_M_manager*) #ifdef FP_IN_IRAM *libgcc.a:*f2.o(.literal .text) From 667de4b9e0f6ce881ee9a14906f28702a43e3c5c Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Fri, 6 Nov 2026 22:48:20 +0300 Subject: [PATCH 5/6] FunctionalInterrupt - private scope internal structs & funcs --- cores/esp8266/FunctionalInterrupt.cpp | 9 +++++++-- cores/esp8266/FunctionalInterrupt.h | 5 ----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp index 16f8fef604..cba4d0e0a7 100644 --- a/cores/esp8266/FunctionalInterrupt.cpp +++ b/cores/esp8266/FunctionalInterrupt.cpp @@ -13,13 +13,16 @@ extern "C" { namespace esp8266::FunctionalInterrupt { namespace { +struct FunctionInfo { + Function reqFunction; + ScheduledFunction reqScheduledFunction; +}; + struct ArgStructure { InterruptInfo* interruptInfo; FunctionInfo* functionInfo; }; -} - void IRAM_ATTR updateArg(void* varg, uint8_t i, uint8_t value, uint32_t micro) { ArgStructure* arg = reinterpret_cast(varg); if (arg && arg->interruptInfo) @@ -42,6 +45,8 @@ void IRAM_ATTR dispatchScheduled(void* varg) schedule_function(std::bind(arg->functionInfo->reqScheduledFunction, InterruptInfo(*(arg->interruptInfo)))); } +} + // see core_esp8266_wiring_digital.cpp, these are referenced there void IRAM_ATTR cleanup(void* varg) { diff --git a/cores/esp8266/FunctionalInterrupt.h b/cores/esp8266/FunctionalInterrupt.h index ad6bb73472..5bd1fa4673 100644 --- a/cores/esp8266/FunctionalInterrupt.h +++ b/cores/esp8266/FunctionalInterrupt.h @@ -23,11 +23,6 @@ struct InterruptInfo { using ScheduledFunction = std::function; -struct FunctionInfo { - Function reqFunction; - ScheduledFunction reqScheduledFunction; -}; - } // namespace esp8266::FunctionalInterrupt void attachInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::Function, int mode); From 98e60f3970559f88bc3c70c54fa62882e1a438d4 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Fri, 6 Nov 2026 22:07:36 +0300 Subject: [PATCH 6/6] Lib - ::delete in IRAM note that the same restrictions as w/ free also apply here specific use-case for right now is detaching interrupt callers allocating things is a whole other matter --- cores/esp8266/abi.cpp | 20 +++++++++++++++++++- tools/sdk/ld/eagle.app.v6.common.ld.h | 4 +++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/cores/esp8266/abi.cpp b/cores/esp8266/abi.cpp index 72f3fb0a06..e6022d5abb 100644 --- a/cores/esp8266/abi.cpp +++ b/cores/esp8266/abi.cpp @@ -21,6 +21,8 @@ #include #include +#include + using __cxxabiv1::__guard; // Debugging helper, last allocation which returned NULL @@ -33,7 +35,7 @@ extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__)); #if !defined(__cpp_exceptions) -// overwrite weak operators new/new[] definitions +// overwrite weak operators new/new[] void* operator new(size_t size) { @@ -79,6 +81,22 @@ void* operator new[] (size_t size, const std::nothrow_t&) #endif // !defined(__cpp_exceptions) +// overwrite weak operators delete/sized delete/delete[] +// todo: current implementation simply links std::free +// todo: functionalinterrupt needs this to be in iram + +void IRAM_ATTR operator delete(void* ptr) noexcept +{ + free(ptr); +} + +void IRAM_ATTR operator delete(void* ptr, size_t) noexcept +{ + ::delete(ptr); +} + +// pure virtual functions + void __cxa_pure_virtual(void) { panic(); diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.h b/tools/sdk/ld/eagle.app.v6.common.ld.h index 450b7f102c..bfde051f16 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -140,11 +140,13 @@ SECTIONS *(.text.gdbstub* .text.gdb_init) /* Any GDB hooks */ - /* functional interrupt placed in IRAM */ + /* functional interrupt & related funcs placed in IRAM */ *(.text._ZNKSt8functionIFvN7esp826619FunctionalInterrupt3TagEE*) *(.text._ZNSt17_Function_handlerIFvN7esp826619FunctionalInterrupt3TagEE*_M_invoke*) *(.text._ZNSt17_Function_handlerIFvN7esp826619FunctionalInterrupt3TagEE*_M_manager*) + *(.text._ZNSt14_Function_baseD2Ev) /* std::function dtor */ + #ifdef FP_IN_IRAM *libgcc.a:*f2.o(.literal .text) *libgcc.a:*f3.o(.literal .text)