{"id":34674937,"url":"https://github.com/ty7swkr/cppurcu","last_synced_at":"2026-05-28T19:02:09.671Z","repository":{"id":322433082,"uuid":"1084239238","full_name":"ty7swkr/cppurcu","owner":"ty7swkr","description":"C++ RCU: header-only, dependency-free, RAII-based snapshot isolation","archived":false,"fork":false,"pushed_at":"2025-12-14T06:18:49.000Z","size":178,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-16T09:10:01.112Z","etag":null,"topics":["cpp","cpp17","header-only","lock-free","rcu","user-space"],"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/ty7swkr.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":"2025-10-27T12:17:49.000Z","updated_at":"2025-12-14T06:18:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ty7swkr/cppurcu","commit_stats":null,"previous_names":["ty7swkr/cppurcu"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ty7swkr/cppurcu","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ty7swkr%2Fcppurcu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ty7swkr%2Fcppurcu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ty7swkr%2Fcppurcu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ty7swkr%2Fcppurcu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ty7swkr","download_url":"https://codeload.github.com/ty7swkr/cppurcu/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ty7swkr%2Fcppurcu/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28007938,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-24T02:00:07.193Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["cpp","cpp17","header-only","lock-free","rcu","user-space"],"created_at":"2025-12-24T20:21:38.474Z","updated_at":"2025-12-24T20:21:41.493Z","avatar_url":"https://github.com/ty7swkr.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cppurcu\n\nA simple implementation of the C++ RCU (read-copy-update) user-space library with RAII-based snapshot isolation, header-only and dependency-free, using only the C++17 standard library.\n\u003cbr\u003e\n\u003cbr\u003e\n\n## Key Features\n\n- **Header-Only**: Works with standard library only, no external dependencies\n- **Lock-Free Reads**: Implemented so that contention is minimized on the read path after cache warm-up.\n- **Snapshot Isolation**: RAII guard pattern for snapshot isolation in the calling thread.\u003cbr\u003e\n(guard_pack loads multiple storages in a single line.)\n- **No Data Duplication**: Data is not deep-copied per thread\n- **Optional Background Destruction**: reclaimer_thread can offload object destruction to a separate thread, reducing burden on reader threads\n\n## Performance\nFor benchmark results, see [PERFORMANCE.md](docs/PERFORMANCE.md).\n\u003cbr\u003e\n\u003cbr\u003e\n\n## Quick Start\n### API Overview\nNo reader registration, grace period management, or memory barriers required.\u003cbr\u003e\n```cpp\nstorage = new_storage;      // Update example (std::shared_ptr\u003cT\u003e new_storage)\nauto data = storage.load(); // Returns guard object\n```\n*Note1: Unlike traditional RCU, cppurcu does not directly reclaim the old data,\nbut delegates reclamation to `std::shared_ptr`.\nThe `std::shared_ptr` reference to the old object is released upon the first `load()` call within the scope.\nTherefore, memory is reclaimed when all references to all `std::shared_ptr` instances are released.*\u003cbr\u003e\n\n*Note2: Consequently, update calls are deadlock-free regardless of their location.*\n\n### Basic Usage\n```cpp\n#include \u003ccppurcu/cppurcu.h\u003e\n#include \u003cmemory\u003e\n#include \u003cstring\u003e\n#include \u003cmap\u003e\n\n// Create a storage with initial data\nauto storage = cppurcu::create(std::make_shared\u003cstd::map\u003cstd::string, std::string\u003e\u003e());\n\n// Read (lock-free) - returns a guard object\nauto data = storage.load(); // cppurcu::guard\u003cT\u003e\nif (data-\u003ecount(\"key\") \u003e 0) {\n  // Use the data\n}\n\n// Update (immediate, delegates reclamation to std::shared_ptr)\nauto new_data = std::make_shared\u003cstd::map\u003cstd::string, std::string\u003e\u003e();\n(*new_data)[\"key\"] = \"value\";\nstorage = new_data; // or storage.update(new_data);\n```\n**⚠️ Important:**\n- The `storage\u003cT\u003e` instance must outlive all threads that use it\n- Destroying `storage` while threads are still accessing it results in undefined behavior\n- Typically, declare `storage` as a global, static, or long-lived member variable\n\n### Snapshot Isolation\nEven when multiple `storage::load()` calls occur across complex call chains within a specific scope in the same thread, or when data updates occur from other threads, all read operations within that thread are enforced to see the same data version.\n\nWhen all guards are destroyed, the next load() gets the updated version\n```cpp\n{\n  auto data = storage.load();    // Snapshot version 1\n  {\n    storage.update(new_data2);   // Update to version 2\n    auto data1 = storage.load(); // Still Snapshot version 1\n  } // data1 Guard destroyed\n} // data Guard destroyed\n\nstorage.update(new_data3);       // Update to version 3\n{\n  auto data = storage.load();    // Loads version 3\n}\n```\n### Multi-Storage Snapshot\n\nWhen you need to load (snapshot) multiple storages in a single line, you can use `cppurcu::load(storage\u003cTs\u003e\u0026...)` as follows:\n```cpp\n#include \u003ccppurcu/cppurcu.h\u003e\n\n// Multiple data storages\nauto storageA = cppurcu::create(...);\nauto storageB = cppurcu::create(...);\nauto storageC = cppurcu::create(...);\n\n// Load all – maintains the same snapshot point\nconst auto \u0026[a, b, c] = cppurcu::load(storageA, storageB, storageC);\n\n// All reads within this scope see consistent data,\n// even if updates occur from other threads.\na-\u003elookup(...);\nb-\u003equery(...);\nc-\u003efind(...);\n```\nIf you need a consistent snapshot within a specific scope, you can write code like this:\n```cpp\n#include \u003ccppurcu/cppurcu.h\u003e\n...........\n{\n  auto pack = cppurcu::load(storageA, storageB, storageC);\n  // or `guard\u003cT\u003e` object from storage\u003cT\u003e::load()\n  auto pack = cppurcu::make_guard_pack(storageA.load(), storageB.load(), storageC.load());\n  .....\n  // Even if storageA and storageC are used somewhere in the call chain \n  // within the calculate(...) function,\n  // the pack maintains a consistent snapshot of storageA, B, and C.\n  my_class.calculate(...);\n  .....\n}\n```\n\n**Note:**\u003cbr\u003e\n- Each storage is still versioned independently; `guard_pack` is an RAII helper for convenient multi-storage snapshot loading, not a cross-storage transaction mechanism.\n\n### With Background Destruction (Optional)\n\nFor objects with expensive destructors, you can use a reclaimer_thread to handle destruction in the background:\n```cpp\n#include \u003ccppurcu/cppurcu.h\u003e\n\n// Create a reclaimer_thread\nauto reclaimer = std::make_shared\u003ccppurcu::reclaimer_thread\u003e();\n\n// Create storage with reclaimer_thread\nauto storage1 = cppurcu::create(std::make_shared\u003cstd::set\u003cstd::string\u003e\u003e(), reclaimer);\n\n// reclaimer_thread can be used regardless of template type.\nauto storage2 = cppurcu::create(std::make_shared\u003cstd::vector\u003cint\u003e\u003e(), reclaimer);\n\n// Somewhere in the source...(update)\nstorage1 = new_data;\n\n// calls load, it gets the updated object.\n// The old object is destroyed in the background thread(reclaimer_thread)\nauto data = storage1.load();\n\n```\n**Note (behavior change)**\u003cbr\u003e\n- Previously, the system used the `reclaimer_thread` (and its mutex) to handle the previous data when updating with new data.\n- Currently, the reader no longer uses the `reclaimer_thread` (or its mutex); the `reclaimer_thread` is only used by `storage\u003cT\u003e::update()`.\n\u003cbr\u003e\n\n## API Reference\n\nFor detailed API documentation, see [API.md](docs/API.md).\n\nQuick reference:\n- `cppurcu::storage\u003cT\u003e` - Main RCU-protected data storage\n- `cppurcu::guard\u003cT\u003e` - RAII guard for snapshot isolation\n- `cppurcu::guard_pack\u003cTs...\u003e` - Multi-storage snapshot helper\n- `cppurcu::reclaimer_thread` - Background destruction handler\n\u003cbr\u003e\n\n## Installation\n\ncppurcu is a header-only library. Copy the cppurcu/ directory to your include path:\n\n```bash\n# Copy headers to your project\ncp -r cppurcu/ /path/to/your/project/include/\n\n# Or add to your include path\ng++ -I./path/to/cppurcu your_code.cpp\n```\n\n### Requirements\n\n- C++17 or later\n\u003cbr\u003e\n\n## Building the Tests\n\nTo build and run the included tests:\n\n```bash\n# Build with cppurcu and mutex\nmake\n\n# Build with liburcu comparison (requires liburcu-dev)\nmake liburcu\n\n# Run Tests\n./rcu_bench 1000000  # Test with 1M items, memory required is 20GB\n```\n\n### Makefile Options\n\n- `make` - Builds tests with cppurcu and mutex (default)\n- `make liburcu` - Builds tests including liburcu comparison\n- `make clean` - Removes build artifacts\n\u003cbr\u003e\n\n## How It Works\n\nMain classes:\n\n1. **`storage\u003cT\u003e`**: User-facing API integrating source, local, and guard\n2. **`guard\u003cT\u003e`**: Return value of storage\u003cT\u003e::load(), RAII guard for snapshot isolation\n3. **`source\u003cT\u003e`**: Maintains the authoritative data and version counter\n4. **`local\u003cT\u003e`**: Thread-local caching (shallow copy only)\n5. **`reclaimer_thread (optional)`**: Background thread for handling object destruction.\n\nThis design does not use:\n- ABA problem solutions (no tagged pointers)\n- Hazard pointers\n- Epoch-based reclaimer\n\n### Read Path\n\nWhen creating a guard (each `load()` call):\n1. Check cached version against source version (skipped if guard\u003cT\u003e.ref_count \u003e 0 for snapshot isolation)\n2. If unchanged: return cached raw pointer (fast path)\n3. If changed: Updates the version, shared_ptr and raw pointers in the cache (slow path)\n\u003cbr\u003eIf reclaimer_thread enabled: push old value to reclaimer queue\n\n### Reclaimer Thread (Optional)\n\nWhen enabled, reclaimer_thread handles object destruction in the background:\n1. storage::load() pushes old shared_ptrs to the reclaim queue when data is updated\n2. Worker thread scans periodically and removes entries when unique()\n3. Non-unique objects remain in the queue until they become reclaimable\n4. Objects are destroyed without blocking readers, reducing overhead for expensive destructors\n\n### Thread Safety Guarantees\n\n- Lock-free reads\n- Thread-safe updates\n- **Requirement**: `storage\u003cT\u003e` lifetime \u003e thread lifetime\n\u003cbr\u003e\n\n## Tests\n\nThe included tests compare three approaches:\n\n1. **std::mutex** - Traditional lock-based protection\n2. **cppurcu** - This library\n3. **liburcu** - Widely-used RCU library (optional)\n\nRun tests with different data sizes:\n```bash\n./rcu_bench 1000      # 1K items\n./rcu_bench 100000    # 100K items\n./rcu_bench 1000000   # 1M items, memory required is 20GB\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fty7swkr%2Fcppurcu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fty7swkr%2Fcppurcu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fty7swkr%2Fcppurcu/lists"}