Repository navigation
✨ (minor) v6: Add hal::thread_safe_i2c - #117
Merged
Merged
Conversation
A hal::i2c that holds a hal::basic_lock around each configure() and transaction() it forwards to a bus, so device drivers in different contexts take turns on it. Platform I2C drivers no longer need to take a lock themselves, and programs that run in one context skip it entirely. Like hal::spi_bus_manager, it either refers to a bus or owns one built in place by a factory, which keeps every user of the bus behind the lock.
This was referenced Oct 26, 2026
hal::i2c no longer has configure() (libhal/libhal#262), so hal::locked_i2c forwards transactions only, and the test fakes no longer override driver_configure(). Requires libhal 5.0.0-alpha.14. hal::i2c_bus_rate<Devices...>(board_limit) computes a bus's clock rate at compile time: the lowest of the board's limit and each device driver's static constexpr max_clock_rate. A device type that doesn't declare one fails to compile, through the hal::i2c_rated_device concept.
The name now says what the wrapper is for. The class docs explain that it is only as safe as the lock it holds.
Clang now warns when a thread_safe_i2c outlives something its bus factory captured by reference, matching hal::spi_bus_manager.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
hal::thread_safe_i2c, ahal::i2cthat holds ahal::basic_lockaround eachtransaction()it forwards to a bus, andhal::i2c_bus_rate(), which computes a bus's clock rate at compile time from the devices on it. It makes one bus safe to use from several threads or contexts: a transaction that starts while another is running waits for it instead of failing withhal::device_or_resource_busy. Platform I2C drivers then don't take a lock themselves, and programs that run in one context don't pay for one (libhal/libhal-arm-mcu#237 drops it fromhal::stm32f1::i2c).hal::spi_bus_manager, deduction guides pick the type. A factory gives one that owns the bus, built in place by guaranteed copy elision, so non-movable buses work and nothing else can reach the bus around the lock. A bus object gives one that refers to it.thread_safe_i2c_base, so each bus type adds only its storage.[[clang::lifetimebound]], so Clang warns when the object outlives a bus, a lock, or anything the factory captures by reference. It is non-copyable and non-movable.std::lock_guardreleases the lock first.thread_safe_i2cat all.It lives in libhal-util rather than core: unlike
spi_bus_manager, which is the only way to turn araw_spi_busintospi_channels, it is optional. Platform drivers already implementhal::i2cdirectly.An owned bus gets its settings from the factory that builds it, since
hal::i2chas noconfigure()as of libhal 5.0.0-alpha.14 (libhal/libhal#262).hal::i2c_bus_rateEvery device on a bus sees its clock, so the bus can run no faster than its slowest device, and the rate has to be fixed before the first transaction. Device drivers declare
static constexpr hal::hertz max_clock_rate, and the application computes the bus rate from the drivers of every device wired to the bus:max_clock_rate.max_clock_ratefails to compile, through thehal::i2c_rated_deviceconcept.Other changes
test_package's, no longer overridedriver_configure().libhal/[>=5.0.0-alpha.14 <6], the first release withouthal::i2c::configure(). Removing it moveddriver_transaction()to a different vtable slot, so this code can't be built against an earlier release.Testing
New tests in
tests/i2c.test.cpp:thread_safe_i2c, with a lock that records whether it is held when each transaction reaches the bus:thread_safe_i2c<that bus>.i2c_bus_rate:max_clock_rate.All 16 suites pass on the host (macOS, LLVM 23), built against libhal 5.0.0-alpha.14. The package and
test_packagebuild for the host and the stm32f103c8. Code size on a target hasn't been measured, since no demo usesthread_safe_i2cyet.