The previous implementation used a single static SIGALRM handler and one
ITIMER_REAL, so a second IntervalTimer silently clobbered the first
despite the API documenting up to 4 simultaneous timers.
Reimplement with one std::thread per active timer, each invoking its
callback at the requested period via sleep_until. A static slot counter
caps concurrent timers at 4 (matching the Teensy 4.x PIT channels);
begin() returns false once all 4 are in use, and end() frees the slot.
update() retimes the next interval, and a self-end() from within a
callback detaches rather than joining to avoid deadlock.
Because the library now uses std::thread, src/CMakeLists.txt links
Threads::Threads PUBLIC so consumers (e.g. test/blink) link pthread
transitively. Adds a test/bugs regression test covering four concurrent
timers, the max-4 limit, and slot reuse after end().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAsvzgsaayj2nbZnnQGAav
Adds .github/workflows/bugs.yml, which configures, builds, and runs
test/bugs (linked against the local src/). The harness exits non-zero on
any failed check, so regressions in the fixed timing/serial/stream
behaviour now fail CI. Also adds a matching status badge to the README.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAsvzgsaayj2nbZnnQGAav
- Correct typos: debuging, emlation, runnning, simial.
- CMake consumer snippet: note where cmake_declare_and_fetch.cmake.in
comes from, and add cmake_minimum_required + CMAKE_CXX_STANDARD 17 so it
matches the test apps and compiles the C++17 headers.
- Note that the root build only produces the library and point readers to
the standalone test apps to run something.
- Mention supported platforms (Linux, macOS, Windows/MSVC).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAsvzgsaayj2nbZnnQGAav
- nanos() no longer subtracts the start baseline a second time; it now
returns micros()*1000 (core_pins.cpp).
- t_start is declared extern as unsigned to match its definition in
Arduino.cpp; the previous time_t declaration read 8 bytes from a 4-byte
object (UB) and the MSVC branch duplicated the definition (core_pins.cpp).
- HardwareSerial::peek() now checks the simulated-input queue before
falling back to std::cin, matching read() so the Stream parsers work on
queued input (hardware_serial.cpp).
- HardwareSerial::write() returns the number of bytes written (1 / count)
instead of 0, honouring the Print contract (hardware_serial.cpp).
- HardwareSerial::available() returns the queued byte count instead of
collapsing to 0/1 (hardware_serial.cpp).
- Stream::readString()/readStringUntil() re-check the string length each
iteration so the max cap is enforced, and break (not continue) on
timeout to avoid spinning (Stream.cpp).
All 9 checks in test/bugs now pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAsvzgsaayj2nbZnnQGAav
Adds test/bugs, a standalone harness that links against the local src/
(rather than fetching from GitHub like the other test apps) and asserts
the correct behaviour for several bugs: nanos() double-subtracting its
baseline, peek() ignoring the simulated-input queue, readStringUntil()
not honouring its max cap, write() returning 0, and available()
collapsing to a 0/1 boolean. Run: ./bugs < /dev/null
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAsvzgsaayj2nbZnnQGAav