{"id":49100572,"url":"https://github.com/solariun/atomicx","last_synced_at":"2026-04-20T23:07:35.375Z","repository":{"id":44670067,"uuid":"420659693","full_name":"solariun/atomicx","owner":"solariun","description":"Pure C++ non stack displacement that implements cooperative multitask library for SINGLE CORE embedded development on DSPs, Microcontrollers and Processor (ARV, RISCV, ARM(all), TENSY, ESP), while also suitable for applications on Windows, Linux and MacOs and compatible with some RTOSs as well. This library allows full event driven applications while uses SMARTs LOCKS and WAIT/NOTIFY locks to also transport messages, MESSAGE BROKER  is also provided (Those uses Message type size_t message and size_t tags, where tag will give meaning to the message). That implementation also introduce thread safe QUEUE (full object) and smart_ptr (to allow better implementation on minimal environment)","archived":false,"fork":false,"pushed_at":"2026-03-25T20:02:40.000Z","size":2193,"stargazers_count":18,"open_issues_count":3,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-03-26T19:48:10.148Z","etag":null,"topics":["arduino-compatible","arm","avr","cooperative-multitasking","esp-8266","inter-process-notification","ipc","ipn","locks","messageble-locks","minimal-stack-usage","non-displaceable-stack","riscv","rtos","single-core-cable","small-memory-usage","tensy"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/solariun.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-10-24T11:04:09.000Z","updated_at":"2026-03-25T20:02:44.000Z","dependencies_parsed_at":"2022-09-04T19:22:30.742Z","dependency_job_id":null,"html_url":"https://github.com/solariun/atomicx","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/solariun/atomicx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solariun%2Fatomicx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solariun%2Fatomicx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solariun%2Fatomicx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solariun%2Fatomicx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/solariun","download_url":"https://codeload.github.com/solariun/atomicx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solariun%2Fatomicx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32069444,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T21:26:33.338Z","status":"ssl_error","status_checked_at":"2026-04-20T21:26:22.081Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino-compatible","arm","avr","cooperative-multitasking","esp-8266","inter-process-notification","ipc","ipn","locks","messageble-locks","minimal-stack-usage","non-displaceable-stack","riscv","rtos","single-core-cable","small-memory-usage","tensy"],"created_at":"2026-04-20T23:07:34.491Z","updated_at":"2026-04-20T23:07:35.362Z","avatar_url":"https://github.com/solariun.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AtomicX\n\n**Cooperative multitasking for embedded systems and beyond.**\n\n![image](https://user-images.githubusercontent.com/1805792/125191254-6591cf80-e239-11eb-9e89-d7500e793cd4.png)\n\n[![Version](https://img.shields.io/badge/version-1.3.0-blue.svg)](#)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Platform](https://img.shields.io/badge/platform-Arduino%20%7C%20ESP8266%20%7C%20ESP32%20%7C%20STM32%20%7C%20Linux-green.svg)](#supported-platforms)\n\n\u003e **[Architecture \u0026 Design Document](design.md)** — class diagrams, state machines, thread lifecycle, intrusive controller internals, and stack management details.\n\nAtomicX is a general-purpose **cooperative thread library** for embedded applications (single-core or confined within another RTOS). It lets you partition your application into multiple controlled execution contexts using cooperative threads — without requiring an operating system, hardware timers, or dynamic memory (unless you opt in).\n\n---\n\n## Key Features\n\n- **Zero stack displacement** — threads run on the real C stack and only back up the minimum necessary bytes on context switch\n- **Two stack modes** — fixed-size (user-provided buffer, zero heap) or self-managed (auto-resizing via `malloc`)\n- **Portable** — uses only `setjmp`/`longjmp` and `memcpy`; no assembly, no platform-specific code in the core\n- **Rich IPC** — Wait/Notify signaling, thread-safe queues, semaphores, read-write mutexes, data pipes (Send/Receive), and broadcast messaging\n- **RAII wrappers** — `smartMutex` and `smartSemaphore` for automatic resource release\n- **Tiny footprint** — single `.hpp` + `.cpp`, suitable for MCUs with as little as 512 bytes of RAM (e.g., ATtiny85)\n- **Dynamic nice** — optional kernel-managed scheduling that auto-tunes thread timing for best performance\n\n---\n\n## Table of Contents\n\n- [Getting Started](#getting-started)\n- [Quick Example](#quick-example)\n- [How It Works](#how-it-works)\n- [API Reference](#api-reference)\n  - [Thread Lifecycle](#thread-lifecycle)\n  - [Synchronization](#synchronization)\n  - [IPC: Wait/Notify](#ipc-waitnotify)\n  - [IPC: Queues](#ipc-queues)\n  - [IPC: Send/Receive Data Pipes](#ipc-sendreceive-data-pipes)\n  - [Broadcasting](#broadcasting)\n- [Platform Porting](#platform-porting)\n- [Examples](#examples)\n- [Architecture \u0026 Design](#architecture--design)\n- [Supported Platforms](#supported-platforms)\n- [Changelog](#changelog)\n- [License](#license)\n\n---\n\n## Getting Started\n\n### Requirements\n\n- C++11 or later\n- `setjmp.h` support (available on virtually all C/C++ compilers)\n\n### Installation\n\n**Arduino**: Copy the `atomicx/` folder into your Arduino libraries directory, or use the Arduino IDE Library Manager.\n\n**PlatformIO**: Add the library to your `lib/` directory.\n\n**PC / Linux / macOS**: Include `atomicx.hpp` and compile `atomicx.cpp` alongside your project:\n\n```bash\ng++ -std=c++11 -I atomicx/ atomicx/atomicx.cpp main.cpp -o myapp\n```\n\n### Minimal Setup\n\n1. Include the header\n2. Implement two platform functions (`Atomicx_GetTick` and `Atomicx_SleepTick`)\n3. Subclass `thread::atomicx`\n4. Call `atomicx::Start()`\n\n---\n\n## Quick Example\n\n```cpp\n#include \u003ciostream\u003e\n#include \u003csys/time.h\u003e\n#include \u003cunistd.h\u003e\n#include \"atomicx.hpp\"\n\nusing namespace thread;\n\n// --- Platform functions (user must implement) ---\n\natomicx_time Atomicx_GetTick(void) {\n    struct timeval tp;\n    gettimeofday(\u0026tp, NULL);\n    return (atomicx_time)tp.tv_sec * 1000 + tp.tv_usec / 1000;\n}\n\nvoid Atomicx_SleepTick(atomicx_time nSleep) {\n    usleep((useconds_t)nSleep * 1000);\n}\n\n// --- Thread with fixed stack ---\n\nclass Blinker : public atomicx {\npublic:\n    Blinker() : atomicx(stack) { SetNice(500); }\n\n    void run() noexcept override {\n        int count = 0;\n        while (Yield()) {\n            std::cout \u003c\u003c \"Blink \" \u003c\u003c ++count \u003c\u003c std::endl;\n        }\n    }\n\n    void StackOverflowHandler() noexcept override {\n        std::cerr \u003c\u003c \"Stack overflow in Blinker!\" \u003c\u003c std::endl;\n    }\n\n    const char* GetName() override { return \"Blinker\"; }\n\nprivate:\n    uint8_t stack[512] = \"\";\n};\n\n// --- Thread with self-managed (auto) stack ---\n\nclass Counter : public atomicx {\npublic:\n    Counter() : atomicx(128, 64) { SetNice(1000); }\n\n    void run() noexcept override {\n        int n = 0;\n        while (Yield()) {\n            std::cout \u003c\u003c \"Count \" \u003c\u003c ++n \u003c\u003c std::endl;\n        }\n    }\n\n    void StackOverflowHandler() noexcept override {\n        std::cerr \u003c\u003c \"Stack overflow in Counter!\" \u003c\u003c std::endl;\n    }\n\n    const char* GetName() override { return \"Counter\"; }\n};\n\nint main() {\n    Blinker b;\n    Counter c;\n    atomicx::Start();  // blocks here, running all threads cooperatively\n}\n```\n\n---\n\n## How It Works\n\nAtomicX implements **stackful cooperative coroutines**:\n\n1. **Construction** — When you instantiate a thread object, it automatically registers itself into a global intrusive doubly-linked list. No manual registration needed.\n2. **`Start()`** — Enters the kernel loop. The scheduler picks the next thread and either calls `run()` (first time) or restores its context.\n3. **`Yield()`** — The running thread saves its stack segment via `memcpy`, saves its CPU context via `setjmp`, and jumps back to the scheduler via `longjmp`.\n4. **Resume** — The scheduler restores the stack segment and jumps into the thread's saved context. Execution continues right after `Yield()`.\n5. **Destruction** — When the thread object is destroyed, it automatically removes itself from the scheduler's list.\n\n```\nThread A          Scheduler          Thread B\n   │                  │                  │\n   │── Yield() ──────\u003e│                  │\n   │  [save stack]    │                  │\n   │  [setjmp+longjmp]│                  │\n   │                  │── resume ────────\u003e│\n   │                  │  [restore stack] │\n   │                  │  [longjmp]       │\n   │                  │                  │── runs...\n   │                  │\u003c── Yield() ──────│\n   │\u003c── resume ───────│                  │\n   │── runs...        │                  │\n```\n\n\u003e **No preemption.** Threads must call `Yield()` (or `Wait()`, or any blocking IPC call) to give control back to the scheduler. This makes all code between yields **atomic** with respect to other AtomicX threads.\n\nFor comprehensive architecture details, see [`design.md`](design.md).\n\n---\n\n## API Reference\n\n### Thread Lifecycle\n\n#### Creating a Thread\n\nSubclass `atomicx` and implement the required virtual methods:\n\n```cpp\nclass MyThread : public thread::atomicx {\npublic:\n    // Fixed stack: provide a buffer\n    MyThread() : atomicx(stack) { SetNice(100); }\n\n    // OR self-managed stack: initial size + increase pace\n    // MyThread() : atomicx(256, 32) { SetNice(100); }\n\n    void run() noexcept override {\n        // Your thread logic. Call Yield() periodically.\n        while (Yield()) {\n            // do work\n        }\n    }\n\n    void StackOverflowHandler() noexcept override {\n        // Called when stack exceeds buffer (and auto-resize fails)\n    }\n\n    // Optional overrides:\n    const char* GetName() override { return \"MyThread\"; }\n    void finish() noexcept override { /* cleanup after run() returns */ }\n\nprivate:\n    uint8_t stack[512] = \"\";\n};\n```\n\n#### Key Methods\n\n| Method | Description |\n|--------|-------------|\n| `atomicx::Start()` | **Static.** Enters the kernel loop — blocks until all threads finish or deadlock |\n| `Yield(nSleep)` | Context switch. Default sleep = thread's nice value. Pass `0` for immediate return |\n| `YieldNow()` | High-priority yield — this thread gets picked up before normal sleepers |\n| `SetNice(ms)` | Set the default sleep interval between yields (in tick units) |\n| `SetDynamicNice(true)` | Let the kernel auto-tune nice based on actual execution time |\n| `Stop()` / `Resume()` | Suspend / resume the thread |\n| `Restart()` | Calls `finish()` and re-enters `run()` from the beginning |\n| `Detach()` | Calls `finish()`, removes thread from scheduler permanently |\n| `GetID()` | Returns the thread's unique ID (its memory address) |\n| `GetName()` | Returns the thread name (override to customize) |\n| `GetStackSize()` | Allocated stack buffer size |\n| `GetUsedStackSize()` | Actual stack usage from last context switch |\n| `IsStackSelfManaged()` | `true` if using auto-stack mode |\n| `GetStatus()` / `GetSubStatus()` | Current thread state (see state machine in [design.md](design.md)) |\n| `GetCurrentTick()` | Returns the current tick via `Atomicx_GetTick()` |\n| `GetLastUserExecTime()` | How long the thread ran during its last time slice |\n| `GetThreadCount()` | Number of active threads in the system |\n| `IsKernelRunning()` | `true` if `Start()` is currently executing |\n\n#### Iterating All Threads\n\n```cpp\nfor (auto\u0026 th : *atomicx::GetCurrent()) {\n    std::cout \u003c\u003c th.GetName() \u003c\u003c \" stack: \" \u003c\u003c th.GetUsedStackSize()\n              \u003c\u003c \"/\" \u003c\u003c th.GetStackSize() \u003c\u003c std::endl;\n}\n```\n\n### Synchronization\n\n#### Semaphore\n\n```cpp\natomicx::semaphore sem(3);  // max 3 concurrent acquisitions\n\n// In thread:\nif (sem.acquire(1000)) {   // wait up to 1000 ticks\n    // critical section\n    sem.release();\n}\n\n// RAII version:\natomicx::smartSemaphore ss(sem);\nif (ss.acquire()) {\n    // auto-released when ss goes out of scope\n}\n```\n\n| Method | Description |\n|--------|-------------|\n| `semaphore(maxShared)` | Create with max concurrent locks |\n| `acquire(timeout)` | Acquire a slot (0 = wait forever) |\n| `release()` | Release one slot |\n| `GetCount()` | Current acquired count |\n| `GetWaitCount()` | Threads waiting to acquire |\n\n#### Mutex (Read-Write Lock)\n\n```cpp\natomicx::mutex mtx;\n\n// Exclusive lock:\nif (mtx.Lock(1000)) {      // timeout optional\n    // only this thread has access\n    mtx.Unlock();\n}\n\n// Shared lock (multiple readers):\nif (mtx.SharedLock()) {\n    // read-only access, other shared locks allowed\n    mtx.SharedUnlock();\n}\n\n// RAII version:\natomicx::smartMutex sm(mtx);\nif (sm.Lock()) {\n    // auto-unlocked when sm is destroyed\n}\n```\n\n### IPC: Wait/Notify\n\nAny variable's address can be used as a synchronization point. The `tag` parameter adds a channel/meaning layer.\n\n```cpp\nint mySignal;  // the variable itself is just an anchor — its value doesn't matter\n\n// Thread A (consumer): blocks until notified\nsize_t message;\nif (Wait(message, mySignal, /*tag=*/1, /*timeout=*/5000)) {\n    // received notification with message\n}\n\n// Thread B (producer): wakes up Thread A\nsize_t payload = 42;\nNotify(payload, mySignal, /*tag=*/1);\n```\n\n| Method | Description |\n|--------|-------------|\n| `Wait(msg, ref, tag, timeout)` | Block until notified. Returns message via `msg` |\n| `Wait(ref, tag, timeout)` | Block until notified (no message) |\n| `WaitAny(msg, ref, tag, timeout)` | Wait for any tag on `ref`. Returns the actual tag |\n| `Notify(ref, tag)` | Wake one waiting thread + yield |\n| `Notify(msg, ref, tag)` | Wake one + send message + yield |\n| `SafeNotify(ref, tag)` | Wake one thread, **no** yield (use in ISR-like contexts) |\n| `SyncNotify(msg, ref, tag, timeout)` | Wait until a waiter exists, then notify |\n| `LookForWaitings(ref, tag, timeout)` | Block until someone is waiting on ref+tag |\n| `HasWaitings(ref, tag)` | Count of threads waiting on ref+tag |\n| `IsWaiting(ref, tag)` | `true` if at least one waiter exists |\n\n### IPC: Queues\n\nThread-safe, blocking queue built on Wait/Notify:\n\n```cpp\natomicx::queue\u003cint\u003e q(10);  // capacity of 10\n\n// Producer thread:\nq.PushBack(42);       // blocks if full\nq.PushFront(99);      // push to front\n\n// Consumer thread:\nint val = q.Pop();    // blocks if empty\n\nq.GetSize();          // current item count\nq.IsFull();           // true if at capacity\n```\n\n### IPC: Send/Receive Data Pipes\n\nTransfer arbitrary binary data between threads. Built on top of SyncNotify/WaitAny:\n\n```cpp\nstruct SensorData { float temp; float humidity; };\n\nint channel;  // any variable as reference anchor\n\n// Sender thread:\nSensorData data = {23.5f, 65.0f};\nuint16_t sent = Send(channel, (uint8_t*)\u0026data, sizeof(data), Timeout(5000));\n\n// Receiver thread:\nSensorData buf;\nuint16_t received = Receive(channel, (uint8_t*)\u0026buf, sizeof(buf), Timeout(5000));\n```\n\n### Broadcasting\n\nSend messages to all threads that have opted in:\n\n```cpp\n// In your thread class:\nclass MyThread : public atomicx {\n    MyThread() : atomicx(stack) {\n        SetReceiveBroadcast(true);  // opt in\n    }\n\n    void BroadcastHandler(const size_t\u0026 ref, const Message\u0026 msg) override {\n        // handle broadcast: ref, msg.message, msg.tag\n    }\n};\n\n// From any thread:\nBroadcastMessage(SIGNAL_TYPE, {payload, tag});\n```\n\n---\n\n## Platform Porting\n\nTo run AtomicX on any platform, implement these two `extern \"C\"` functions:\n\n```cpp\n// Return the current time in your chosen tick unit (ms, us, etc.)\natomicx_time Atomicx_GetTick(void);\n\n// Sleep/idle for nSleep ticks — opportunity for power saving\nvoid Atomicx_SleepTick(atomicx_time nSleep);\n```\n\n### Arduino Example\n\n```cpp\natomicx_time Atomicx_GetTick(void) {\n    return (atomicx_time)millis();\n}\n\nvoid Atomicx_SleepTick(atomicx_time nSleep) {\n    delay(nSleep);\n}\n```\n\n### POSIX (Linux / macOS) Example\n\n```cpp\natomicx_time Atomicx_GetTick(void) {\n    struct timeval tp;\n    gettimeofday(\u0026tp, NULL);\n    return (atomicx_time)tp.tv_sec * 1000 + tp.tv_usec / 1000;\n}\n\nvoid Atomicx_SleepTick(atomicx_time nSleep) {\n    usleep((useconds_t)nSleep * 1000);\n}\n```\n\n### ESP32 with Power Saving\n\n```cpp\natomicx_time Atomicx_GetTick(void) {\n    return (atomicx_time)millis();\n}\n\nvoid Atomicx_SleepTick(atomicx_time nSleep) {\n    esp_sleep_enable_timer_wakeup(nSleep * 1000);  // light sleep\n    esp_light_sleep_start();\n}\n```\n\n\u003e The `Atomicx_SleepTick` function is called by the scheduler when no thread is ready to run. Use it to reduce power consumption on battery-powered devices.\n\n---\n\n## Examples\n\n### PC\n\n| Example | Description |\n|---------|-------------|\n| [`examples/pc/simple`](examples/pc/simple) | Basic threads with fixed and self-managed stacks |\n| [`examples/pc/semaphore`](examples/pc/semaphore) | Semaphore usage with Send/Receive data pipes |\n\n### Arduino\n\n| Example | Description |\n|---------|-------------|\n| [`examples/Arduino/simple`](examples/Arduino/simple) | Minimal thread example |\n| [`examples/Arduino/semaphore`](examples/Arduino/semaphore) | Counting semaphore demo |\n| [`examples/Arduino/sharedlock`](examples/Arduino/sharedlock) | Read-write mutex (shared lock) |\n| [`examples/Arduino/send_receive`](examples/Arduino/send_receive) | Data pipe transfer between threads |\n| [`examples/Arduino/watchdog`](examples/Arduino/watchdog) | Watchdog pattern using thread monitoring |\n| [`examples/Arduino/DotMatrix`](examples/Arduino/DotMatrix) | Full project: LED matrix scroller with Serial/Telnet terminals, UDP trap, and logging (ESP8266) |\n| [`examples/Arduino/ThermalCameraDemo`](examples/Arduino/ThermalCameraDemo) | Thermal camera display |\n| [`examples/Arduino/avrAutoRobotController`](examples/Arduino/avrAutoRobotController) | Robot controller with IPC motor commands |\n\n---\n\n## Architecture \u0026 Design\n\nFor detailed architecture diagrams, class relationships, state machines, stack management internals, and the intrusive object controller design, see **[`design.md`](design.md)**.\n\n### Key Design Decisions\n\n- **Cooperative, not preemptive** — deterministic behavior, no race conditions between AtomicX threads, no need for critical sections\n- **Intrusive linked list** — zero-allocation thread management; threads register/unregister themselves on construction/destruction\n- **`setjmp`/`longjmp` context switch** — portable across all C compilers, no assembly required\n- **Stack save/restore via `memcpy`** — threads use the real C stack during execution, only backing up the used portion on yield\n- **All synchronization built on Wait/Notify** — semaphores, mutexes, and queues are layered on top of a single primitive, keeping the core small\n\n---\n\n## Supported Platforms\n\n| Platform | Tested | Notes |\n|----------|--------|-------|\n| Arduino AVR (Uno, Mega, ATtiny85) | Yes | Fixed stack recommended |\n| ESP8266 | Yes | Full featured, see DotMatrix example |\n| ESP32 | Yes | Single-core cooperative context |\n| STM32 | Yes | Via Arduino core or bare-metal |\n| Linux / macOS (POSIX) | Yes | Great for development and testing |\n| Any C++11 with `setjmp.h` | Should work | Implement the two platform functions |\n\n---\n\n## Changelog\n\n### Version 1.3.0\n\n- **Send/Receive data pipes** — transfer binary data between threads using `Send()` and `Receive()`, enabling client/server patterns inside embedded applications\n- **WaitAny** — extends Wait/Notify to receive any tag, returning the actual tag by reference\n- **Broadcasting** — replaced the old Broker with `BroadcastMessage()` + `BroadcastHandler()` for async thread-to-thread signaling\n- **Mutex/Semaphore timeouts** — `Lock(timeout)` and `SharedLock(timeout)` now accept optional timeouts (fully backward compatible)\n- **DotMatrix project** — full ESP8266 example with Serial/Telnet terminals, UDP trap, and a logging API\n\n### Version 1.2.1\n\n- **Dynamic Nice** — kernel auto-tunes thread timing via `SetDynamicNice(true)`\n- **`YieldNow()`** — high-priority context switch for time-sensitive threads\n- **`smartSemaphore`** and **`smartMutex`** — RAII wrappers for automatic resource release\n- **Semaphores** — `atomicx::semaphore` with `acquire()`/`release()` and optional timeout\n- **`Timeout` utility class** — `IsTimedout()`, `GetRemaining()`, `GetDurationSince()`\n- **Renamed** `atomicx::lock` to `atomicx::mutex` for consistency\n- **Stack increase pace** — configurable growth rate for self-managed stacks\n\n### Version 1.2.0\n\n- **Self-managed stack** — use `atomicx()` default constructor for automatic stack memory management\n- New examples: Arduino/Simple, avrAutoRobotController\n\n### Version 1.1.3\n\n- Thermal Camera Demo\n- Custom `subType` parameter for Wait/Notify (enables layered notification channels)\n\n### Version 1.1.2\n\n- Split `Notify` into `Notify` and `SyncNotify` to resolve compilation ambiguity on some boards\n\n### Version 1.1.1\n\n- `SyncNotify` — Notify waits for a matching `Wait` to be available before sending\n- `avrRobotController` simulator with terminal interface\n\n### Version 1.1.0\n\n- `finish()` callback after `run()` returns (enables self-destroying eventual threads)\n- `smartMutex` RAII compliance\n- **Timed Wait/Notify** — real state-blocking with timeout, no spin locks\n- `LookForWaitings` — block until a waiter appears for a given ref+tag\n- Tag value `0` matches all tags (wildcard)\n\n### Version 1.0.0\n\n- Initial release\n- Cooperative threading with `setjmp`/`longjmp` context switching\n- Zero stack displacement — threads use the full C stack\n- Wait/Notify IPC with message and tag\n- Thread-safe queues\n- Read-write mutex (Lock / SharedLock)\n- Safe notification variants (no context switch) for use in interrupt-like contexts\n\n---\n\n## Important Notes\n\n- **Stack memory is protected** — each thread's stack context is isolated. Do not pass stack pointers between threads. Use global variables, heap allocations, `smart_ptr`, queues, or `Send`/`Receive` instead.\n- **No spin locks** — all blocking operations use real kernel-level state blocking via the scheduler. Waiting threads consume zero CPU.\n- **Cooperative discipline** — you must call `Yield()` (or any blocking API) regularly. A thread that never yields will starve all others.\n\n---\n\n## License\n\n[MIT License](LICENSE) - Copyright (c) 2022 Gustavo Campos\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolariun%2Fatomicx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsolariun%2Fatomicx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolariun%2Fatomicx/lists"}