From d058be3e0f08144e67d3687da57e98d346f00361 Mon Sep 17 00:00:00 2001 From: Chris Gooch Date: Thu, 19 Aug 2021 19:37:59 +0100 Subject: [PATCH 1/5] minor spelling/grammar fixes to driver polymorphism section, add links to new section in intro --- docs/api/drivers/drivers.md | 32 +++++++++++++++---------------- docs/introduction/introduction.md | 1 + readme.md | 2 +- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/docs/api/drivers/drivers.md b/docs/api/drivers/drivers.md index 045329f6a..0b2288fd1 100644 --- a/docs/api/drivers/drivers.md +++ b/docs/api/drivers/drivers.md @@ -18,19 +18,19 @@ In Mbed OS, the standard API for asynchronous events is the attach function. You One important thing to note is that when you attach callbacks with this function, the driver calls them in interrupt context. Interrupt context runs at a higher priority than any thread, which means that any code called from the attach callback must be interrupt safe. This excludes any blocking APIs such as blocking drivers, malloc, and mutexes. Or you risk preventing other high-priority interrupts from running, which may break other parts of the OS. -When you need to call code that’s not interrupt safe from interrupt context, you must first defer from the interrupt context to a thread. The easiest way to defer to a thread is to signal a waiting thread through a [Semaphore](semaphore.html). In more complex use cases, you can use an [EventQueue](eventqueue.html) to manage multiple events at once. +When you need to call code that's not interrupt safe from interrupt context, you must first defer from the interrupt context to a thread. The easiest way to defer to a thread is to signal a waiting thread through a [Semaphore](semaphore.html). In more complex use cases, you can use an [EventQueue](eventqueue.html) to manage multiple events at once. ## Experimental: Driver Interfaces and Polymorphism -**Please note: Polymorphic driver interfaces are still experimental and must be manually enabled by adding the `EXPERIMENTAL_API` feature to your project in your application configuration.** +**Note: Polymorphic driver interfaces are still experimental and must be manually enabled by adding the `EXPERIMENTAL_API` feature to your project in your application configuration.** ### Motivation and Overview -Mbed driver APIs are intended to provide a solid foundation upon which to build cross-platform third-party libraries and higher level device drivers. With this goal in mind, an experimental feature has been introduced into Mbed's driver API hierarchy: **interfaces and polymorphism** +Mbed driver APIs are intended to provide a solid foundation upon which to build cross-platform third-party libraries and higher level device drivers. With this goal in mind, an experimental feature has been introduced into Mbed's driver API hierarchy: **interfaces and polymorphism**. Previously, polymorphism and virtual inheritance have been avoided in Mbed's core hardware driver APIs due to the potential memory and speed implications. Virtual inheritance adds virtual tables (vtables) to each inheriting class; this results in a slightly larger memory footprint for each instance of a virtual class. Not only this, but when a method is being called on a pointer to a virtual class (and therefore the concrete type of the underlying object is not known at compile time), there is an additional runtime step involved where the correct virtual method must be looked up in the class's vtable. In some high speed use cases, this added latency can be unacceptable. -With the introduction of C++11, the language now has features to help optimize the above situtation. Namely, the `final` keyword. +With the introduction of C++11, the language now has features to help optimize the above situation. Namely, the `final` keyword. Subclasses inheriting from virtual base classes may now be marked `final`, which seals them from further inheritance. This allows the compiler to optimize calls to virtual methods in some cases, including inlining and removing the vtable lookup entirely. It must be stressed that this _can_ eliminate the vtable lookup and associated latency, but ultimately it is up to the compiler to do so. @@ -40,13 +40,13 @@ Unfortunately, at time of writing, toolchain support for pruning unused vtables Even though this feature is still experimental, you can start writing code that takes advantage of driver polymorphism now. -As an example use case, consider the scenario where your target has a limited number of internal GPIO available. You have run out of GPIO but need to control an RGB LED for user feedback. Now, your hardware team is very smart, and they've solved the problem for you: they've added an external GPIO expander IC that's accesible over I2C! You've already developed a very well tested and complex LED animation library but it has a structure like so: +As an example use case, consider the scenario where your target has a limited number of internal GPIO available. You have run out of GPIO but need to control an RGB LED for user feedback. Now, your hardware team is very smart, and they've solved the problem for you: they've added an external GPIO expander IC that's accessible over I2C! You've already developed a very well tested and complex LED animation library but it has a structure like so: ``` class LEDAnimator { LEDAnimator(PinName red, PinName green, PinName blue) :_red(red), _green(green), _blue(blue) { ... } - + private: mbed::DigitalOut _red; mbed::DigitalOut _green; @@ -68,7 +68,7 @@ When enabled, supported drivers will inherit from a driver **interface**: ![](../../images/driver_interface_diagram.png) -Note that some behavior _is_ defined in the interface itself, namely, some operators. These operators are defined here to make it intuitive to use _all_ implementations of a given driver interface, even when mixing them together. +Note that some behavior _is_ defined in the interface itself, namely, some operators. These operators are defined here to make it intuitive to use _all_ implementations of a given driver interface, even when mixing them together. You can always evaluate a `mbed::interface::DigtialOut` as an `int` (which returns the state of the output), and you can always assign the value of one `mbed::interface::DigitalOut` to another `mbed::interface::DigitalOut` in an intuitive manner using the assignment "=" operator. @@ -78,7 +78,7 @@ Let's get back to the above example. What you can do now, with driver polymorphi This hierarchy minimizes the changes you need to make to your existing library. -The recommended pattern for designing 3rd-party libraries that take advantage of driver polymorphism is to use _pointers_ to the _interface_ of the driver you're using. For example, we can refactor the `LEDAnimator` class to the following: +The recommended pattern for designing third party libraries that take advantage of driver polymorphism is to use _pointers_ to the _interface_ of the driver you're using. For example, we can refactor the `LEDAnimator` class to the following: ```c++ class LEDAnimator { @@ -96,7 +96,7 @@ class LEDAnimator { LEDAnimator(mbed::interface::DigitalOut *red, mbed::interface::DigitalOut *green, mbed::interface::DigitalOut *blue) : _red(red), _green(green), _blue(blue), _internally_created(false) { ... } - + ~LEDAnimator() { // Make sure to delete the instances if they were dynamically allocated! if(_internally_created) { @@ -105,7 +105,7 @@ class LEDAnimator { delete _blue; } } - + private: mbed::interface::DigitalOut *_red; mbed::interface::DigitalOut *_green; @@ -129,7 +129,7 @@ When `FEATURE_EXPERIMENTAL_API` is disabled, the following type alias is made in using mbed::interface::DigitalOut = mbed::DigitalOut; ``` -This allows your new code library to be compiled with or without driver polymorphism enabled, since `mbed::interface::DigitalOut` will just fall back to the familiar, unpolymorphic `mbed::DigitalOut`. +This allows your new code library to be compiled with or without driver polymorphism enabled, since `mbed::interface::DigitalOut` will just fall back to the familiar, non-polymorphic `mbed::DigitalOut`. ### Design Considerations and Caveats @@ -139,15 +139,15 @@ In some cases, such as a software ("bit-banged") UART implementation or similar, Fortunately, you can still take advantage of driver polymorphism _and_ maintain high speed operation when you need it. -When driver polymorphism is enabled, Mbed's driver implementations are marked `final`. This accomplishes two things: +When driver polymorphism is enabled, Mbed's driver implementations are marked `final`. This accomplishes two things: -1. It prevents any further inheritance from the `final` class. +1. It prevents any further inheritance from the `final` class. 2. It hints to the compiler that a pointer or reference to this class will never point to anything but an instance of this class. The latter effect allows us to minimize the impact of virtual inheritance on execution speed in some cases. If the compiler knows the concrete type of the driver object at runtime, it _may_ eliminate or inline the vtable redirection. In our testing, we have seen this optimization result in _no speed hit_ when toggling a pin on and off in a while loop. Your results may vary depending on your target's HAL implementation, however. -To improve the compiler's ability to optimize calls to a polymorphic driver in this way, you should use the following guidelines when designing your 3rd-party libraries: +To improve the compiler's ability to optimize calls to a polymorphic driver in this way, you should use the following guidelines when designing your third party libraries: -When your library **does not** have any high speed requirements, you should use a pointer/reference to the _driver interface_ (eg: `mbed::interface::DigitalOut*`). This gives your library maximum flexibility by allowing it to use polymorphic subtypes of the given interface. +When your library **does not** have any high speed requirements, you should use a pointer/reference to the _driver interface_ (for example: `mbed::interface::DigitalOut*`). This gives your library maximum flexibility by allowing it to use polymorphic subtypes of the given interface. -When your library **does** have high speed requirements (such as the "bit-banged" example mentioned previously), you should use a pointer/reference to Mbed's driver implementation type _directly_ (eg: `mbed::DigitalOut*`). Since the compiler knows the concrete type is Mbed's implementation (which is `final`), it _may_ inline calls to that type, maintaining the execution speed possible with polymorphism disabled. +When your library **does** have high speed requirements (such as the "bit-banged" example mentioned previously), you should use a pointer/reference to Mbed's driver implementation type _directly_ (for example: `mbed::DigitalOut*`). Since the compiler knows the concrete type is Mbed's implementation (which is `final`), it _may_ inline calls to that type, maintaining the execution speed possible with polymorphism disabled. diff --git a/docs/introduction/introduction.md b/docs/introduction/introduction.md index 4c0e6cd2e..143dffd6d 100644 --- a/docs/introduction/introduction.md +++ b/docs/introduction/introduction.md @@ -24,6 +24,7 @@ Our [quick start](../quick-start/index.html) guides show how to build an example ## Recently updated documentation +- We've added new section about driver interfaces and polymorphism to the [Drivers overview](../apis/drivers-concepts.html) page, and also made minor updates to the FPGA tests section of [Static pin map extension](../porting/static-pinmap-port.html), [build tools overview](..build-tools/index.html) and [Mbed CLI 2](../build-tools/mbed-cli-2.html). - A new section about [optimizing for throughput and power consumption](../apis/optimizing-applications-for-throughput-and-power-consumption.html) with Bluetooth Low Energy (BLE). We've also removed old material relating to services which are no longer supported and instead provided links to the [Experimental services for Mbed OS BLE](../apis/ble-services.html) repository. - We've updated the [unit testing](../debug-test/unit-testing.html) information to reflect changes to the version of CMake used. You will need to update to CMake version 3.19 or above and alter your test code structure. - A new [Mbed CLI1 to CLI2 migration guide](../build-tools/migration-guide.html). diff --git a/readme.md b/readme.md index 1120e97a8..044099ebf 100644 --- a/readme.md +++ b/readme.md @@ -7,6 +7,6 @@ If you want to contribute, please read our [contribution guide](https://os.mbed. Please open pull requests against either: - `development` - this is our development branch. -- `6.12` - this is the latest live version. +- `6.13` - this is the latest live version. Older version branches probably don't require fixes; please open an issue if you think we're wrong. From 88a3f413581b7325f484092f670aa31db6552d56 Mon Sep 17 00:00:00 2001 From: Chris Gooch Date: Thu, 19 Aug 2021 20:02:14 +0100 Subject: [PATCH 2/5] changes to intro section --- docs/introduction/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction/introduction.md b/docs/introduction/introduction.md index 143dffd6d..6058026f8 100644 --- a/docs/introduction/introduction.md +++ b/docs/introduction/introduction.md @@ -24,7 +24,7 @@ Our [quick start](../quick-start/index.html) guides show how to build an example ## Recently updated documentation -- We've added new section about driver interfaces and polymorphism to the [Drivers overview](../apis/drivers-concepts.html) page, and also made minor updates to the FPGA tests section of [Static pin map extension](../porting/static-pinmap-port.html), [build tools overview](..build-tools/index.html) and [Mbed CLI 2](../build-tools/mbed-cli-2.html). +- We've added a section describing driver interfaces and polymorphism to the [Drivers overview](../apis/drivers-concepts.html) page. Polymorphic driver interfaces in MbedOS are still experimental, and not enabled by default. You can start writing code that takes advantage of driver polymorphism now, but be sure to read the notes on backwards compatibility and caveats. - A new section about [optimizing for throughput and power consumption](../apis/optimizing-applications-for-throughput-and-power-consumption.html) with Bluetooth Low Energy (BLE). We've also removed old material relating to services which are no longer supported and instead provided links to the [Experimental services for Mbed OS BLE](../apis/ble-services.html) repository. - We've updated the [unit testing](../debug-test/unit-testing.html) information to reflect changes to the version of CMake used. You will need to update to CMake version 3.19 or above and alter your test code structure. - A new [Mbed CLI1 to CLI2 migration guide](../build-tools/migration-guide.html). From 1b1bb144a316c5350ba9c3ffe6e9e0f9c93e88ea Mon Sep 17 00:00:00 2001 From: Chris Gooch Date: Fri, 20 Aug 2021 11:08:58 +0100 Subject: [PATCH 3/5] move polymorphic drivers section to its own page --- docs/api/drivers/driver_interfaces.md | 131 ++++++++++++++++++++++++++ docs/api/drivers/drivers.md | 4 + docs/introduction/introduction.md | 2 +- 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 docs/api/drivers/driver_interfaces.md diff --git a/docs/api/drivers/driver_interfaces.md b/docs/api/drivers/driver_interfaces.md new file mode 100644 index 000000000..d4d1df77e --- /dev/null +++ b/docs/api/drivers/driver_interfaces.md @@ -0,0 +1,131 @@ +# Driver Interfaces (Experimental) + +**Note: Polymorphic driver interfaces are still experimental and must be manually enabled by adding the `EXPERIMENTAL_API` feature to your project in your application configuration.** + +## Motivation and Overview + +Mbed driver APIs are intended to provide a solid foundation upon which to build cross-platform third-party libraries and higher level device drivers. With this goal in mind, an experimental feature has been introduced into Mbed's driver API hierarchy: **interfaces and polymorphism**. + +Previously, polymorphism and virtual inheritance have been avoided in Mbed's core hardware driver APIs due to the potential memory and speed implications. Virtual inheritance adds virtual tables (vtables) to each inheriting class; this results in a slightly larger memory footprint for each instance of a virtual class. Not only this, but when a method is being called on a pointer to a virtual class (and therefore the concrete type of the underlying object is not known at compile time), there is an additional runtime step involved where the correct virtual method must be looked up in the class's vtable. In some high speed use cases, this added latency can be unacceptable. + +With the introduction of C++11, the language now has features to help optimize the above situation. Namely, the `final` keyword. + +Subclasses inheriting from virtual base classes may now be marked `final`, which seals them from further inheritance. This allows the compiler to optimize calls to virtual methods in some cases, including inlining and removing the vtable lookup entirely. It must be stressed that this _can_ eliminate the vtable lookup and associated latency, but ultimately it is up to the compiler to do so. + +Unfortunately, at time of writing, toolchain support for pruning unused vtables is not widely supported and so the memory usage increase associated with virtual inheritance is unavoidable. This is the primary reason Mbed's developers have decided to make this feature experimental and disabled by default. For some very memory-constrained targets this memory usage increase would be unacceptable. + +## Using Driver Polymorphism in Your Code + +Even though this feature is still experimental, you can start writing code that takes advantage of driver polymorphism now. + +As an example use case, consider the scenario where your target has a limited number of internal GPIO available. You have run out of GPIO but need to control an RGB LED for user feedback. Now, your hardware team is very smart, and they've solved the problem for you: they've added an external GPIO expander IC that's accessible over I2C! You've already developed a very well tested and complex LED animation library but it has a structure like so: + +``` +class LEDAnimator { + + LEDAnimator(PinName red, PinName green, PinName blue) :_red(red), _green(green), _blue(blue) { ... } + +private: + mbed::DigitalOut _red; + mbed::DigitalOut _green; + mbed::DigitalOut _blue; + +} +``` + +As it is now, your library only supports using internal GPIO. This obviously won't work with the external GPIO expander IC! + +Previously, your only options were: + +1. Rewrite your animation library to use the I2C-based GPIO expander. This creates an entirely separate fork of your library to maintain in the best case. +2. Add extra code to conditionally handle the case where your library is using an external I2C-connected GPIO expander rather than internal GPIO. This complicates your library and makes the code much less readable. Additionally, what happens when your hardware team uses a SPI-connected GPIO expander in the next design? + +With driver polymorphism, the problem is much easier to solve. + +When enabled, supported drivers will inherit from a driver **interface**: + +![](../../images/driver_interface_diagram.png) + +Note that some behavior _is_ defined in the interface itself, namely, some operators. These operators are defined here to make it intuitive to use _all_ implementations of a given driver interface, even when mixing them together. + +You can always evaluate a `mbed::interface::DigtialOut` as an `int` (which returns the state of the output), and you can always assign the value of one `mbed::interface::DigitalOut` to another `mbed::interface::DigitalOut` in an intuitive manner using the assignment "=" operator. + +Let's get back to the above example. What you can do now, with driver polymorphism, is make it possible for your external I2C-connected GPIO expander to _act like_ a `DigitalOut` and _be compatible_ with any API that uses `mbed::interface:DigitalOut`: + +![](../../images/driver_polymorphism_example.png) + +This hierarchy minimizes the changes you need to make to your existing library. + +The recommended pattern for designing third party libraries that take advantage of driver polymorphism is to use _pointers_ to the _interface_ of the driver you're using. For example, we can refactor the `LEDAnimator` class to the following: + +```c++ +class LEDAnimator { + + /** + * Optional constructor to maintain backwards compatibility. This constructor + * dynamically allocates its own DigitalOut instances internally. + */ + LEDAnimator(PinName red, PinName green, PinName blue) : _internally_created(true) { + _red = new mbed::DigitalOut(red); + _green = new mbed::DigitalOut(green); + _blue = new mbed::DigitalOut(blue); + } + + LEDAnimator(mbed::interface::DigitalOut *red, mbed::interface::DigitalOut *green, mbed::interface::DigitalOut *blue) : _red(red), _green(green), _blue(blue), _internally_created(false) { + ... + } + + ~LEDAnimator() { + // Make sure to delete the instances if they were dynamically allocated! + if(_internally_created) { + delete _red; + delete _green; + delete _blue; + } + } + +private: + mbed::interface::DigitalOut *_red; + mbed::interface::DigitalOut *_green; + mbed::interface::DigitalOut *_blue; + + bool _internally_created; +} +``` + +Now it is possible for your existing library to use the external GPIO expander without any knowledge that it is external. As long as your `my_company::I2CExpander::DigitalOut` class implements the `mbed::interface::DigitalOut` interface properly, your code will work as it did before. You can even have some of the GPIO be internal or external if your design calls for it! + +## Backwards Compatibility + +Since driver polymorphism is still experimental, it is disabled by default. Therefore, Mbed's developers have been careful to make sure any code using the existing, non-polymorphic driver classes is not affected by the introduction of this new class hierarchy. + +The inheritance of a driver from the new driver interface is conditional upon `FEATURE_EXPERIMENTAL_API` being added to your project's configuration. However, it is still possible for you to develop APIs now that are compatible with both polymorphic and non-polymorphic drivers. + +When `FEATURE_EXPERIMENTAL_API` is disabled, the following type alias is made instead: + +```c++ +using mbed::interface::DigitalOut = mbed::DigitalOut; +``` + +This allows your new code library to be compiled with or without driver polymorphism enabled, since `mbed::interface::DigitalOut` will just fall back to the familiar, non-polymorphic `mbed::DigitalOut`. + +## Design Considerations and Caveats + +As mentioned before, virtual inheritance and polymorphism _do_ have an impact on the resulting binary. For extremely constrained targets, you can optimize away a bit of memory consumption by disabling driver polymorphism (which is currently the default). Then there is the impact on execution speed that can result from virtual inheritance. + +In some cases, such as a software ("bit-banged") UART implementation or similar, a small change in execution speed can have disastrous effects on your code's timing. + +Fortunately, you can still take advantage of driver polymorphism _and_ maintain high speed operation when you need it. + +When driver polymorphism is enabled, Mbed's driver implementations are marked `final`. This accomplishes two things: + +1. It prevents any further inheritance from the `final` class. +2. It hints to the compiler that a pointer or reference to this class will never point to anything but an instance of this class. + +The latter effect allows us to minimize the impact of virtual inheritance on execution speed in some cases. If the compiler knows the concrete type of the driver object at runtime, it _may_ eliminate or inline the vtable redirection. In our testing, we have seen this optimization result in _no speed hit_ when toggling a pin on and off in a while loop. Your results may vary depending on your target's HAL implementation, however. + +To improve the compiler's ability to optimize calls to a polymorphic driver in this way, you should use the following guidelines when designing your third party libraries: + +When your library **does not** have any high speed requirements, you should use a pointer/reference to the _driver interface_ (for example: `mbed::interface::DigitalOut*`). This gives your library maximum flexibility by allowing it to use polymorphic subtypes of the given interface. + +When your library **does** have high speed requirements (such as the "bit-banged" example mentioned previously), you should use a pointer/reference to Mbed's driver implementation type _directly_ (for example: `mbed::DigitalOut*`). Since the compiler knows the concrete type is Mbed's implementation (which is `final`), it _may_ inline calls to that type, maintaining the execution speed possible with polymorphism disabled. diff --git a/docs/api/drivers/drivers.md b/docs/api/drivers/drivers.md index 0b2288fd1..f83bf9cbf 100644 --- a/docs/api/drivers/drivers.md +++ b/docs/api/drivers/drivers.md @@ -20,6 +20,8 @@ One important thing to note is that when you attach callbacks with this function When you need to call code that's not interrupt safe from interrupt context, you must first defer from the interrupt context to a thread. The easiest way to defer to a thread is to signal a waiting thread through a [Semaphore](semaphore.html). In more complex use cases, you can use an [EventQueue](eventqueue.html) to manage multiple events at once. + diff --git a/docs/introduction/introduction.md b/docs/introduction/introduction.md index 6058026f8..03c57e9b4 100644 --- a/docs/introduction/introduction.md +++ b/docs/introduction/introduction.md @@ -24,7 +24,7 @@ Our [quick start](../quick-start/index.html) guides show how to build an example ## Recently updated documentation -- We've added a section describing driver interfaces and polymorphism to the [Drivers overview](../apis/drivers-concepts.html) page. Polymorphic driver interfaces in MbedOS are still experimental, and not enabled by default. You can start writing code that takes advantage of driver polymorphism now, but be sure to read the notes on backwards compatibility and caveats. +- We've added a section describing [driver interfaces and polymorphism](../apis/driver-interfaces-experimental.html). Polymorphic driver interfaces in MbedOS are still experimental, and not enabled by default. You can start writing code that takes advantage of driver polymorphism now, but be sure to read the notes on backwards compatibility and caveats. - A new section about [optimizing for throughput and power consumption](../apis/optimizing-applications-for-throughput-and-power-consumption.html) with Bluetooth Low Energy (BLE). We've also removed old material relating to services which are no longer supported and instead provided links to the [Experimental services for Mbed OS BLE](../apis/ble-services.html) repository. - We've updated the [unit testing](../debug-test/unit-testing.html) information to reflect changes to the version of CMake used. You will need to update to CMake version 3.19 or above and alter your test code structure. - A new [Mbed CLI1 to CLI2 migration guide](../build-tools/migration-guide.html). From fce78b87ccafafe371565fe774a0840e65cb0db2 Mon Sep 17 00:00:00 2001 From: Chris Gooch Date: Fri, 20 Aug 2021 11:23:30 +0100 Subject: [PATCH 4/5] update docs.json for new section --- docs.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs.json b/docs.json index 18143b565..e51e577c3 100644 --- a/docs.json +++ b/docs.json @@ -236,7 +236,9 @@ "intro": { "path": "docs/api/drivers/drivers.md" }, - "sources": [] + "sources": [{ + "path": "docs/api/drivers/driver_interfaces.md" + }] }, { "title": "Drivers architecture", From 8359a3813166362ccbb862e2121a16c4ea98850a Mon Sep 17 00:00:00 2001 From: Chris Gooch Date: Fri, 20 Aug 2021 11:41:32 +0100 Subject: [PATCH 5/5] remove material commented out in previous commit --- docs/api/drivers/drivers.md | 136 ------------------------------------ 1 file changed, 136 deletions(-) diff --git a/docs/api/drivers/drivers.md b/docs/api/drivers/drivers.md index f83bf9cbf..9c6a00e83 100644 --- a/docs/api/drivers/drivers.md +++ b/docs/api/drivers/drivers.md @@ -19,139 +19,3 @@ In Mbed OS, the standard API for asynchronous events is the attach function. You One important thing to note is that when you attach callbacks with this function, the driver calls them in interrupt context. Interrupt context runs at a higher priority than any thread, which means that any code called from the attach callback must be interrupt safe. This excludes any blocking APIs such as blocking drivers, malloc, and mutexes. Or you risk preventing other high-priority interrupts from running, which may break other parts of the OS. When you need to call code that's not interrupt safe from interrupt context, you must first defer from the interrupt context to a thread. The easiest way to defer to a thread is to signal a waiting thread through a [Semaphore](semaphore.html). In more complex use cases, you can use an [EventQueue](eventqueue.html) to manage multiple events at once. - -