{"id":19735993,"url":"https://github.com/siddiqsoft/rwlenvelope","last_synced_at":"2026-05-22T04:01:56.535Z","repository":{"id":142736447,"uuid":"388359489","full_name":"SiddiqSoft/rwlenvelope","owner":"SiddiqSoft","description":"Read-write envelope class","archived":false,"fork":false,"pushed_at":"2026-04-29T21:28:23.000Z","size":109,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-29T22:31:03.102Z","etag":null,"topics":["cpp17","lock","locking","nuget","reader-writer","thread-safe","utility-class","utility-classes"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SiddiqSoft.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-07-22T06:54:04.000Z","updated_at":"2026-04-29T21:24:09.000Z","dependencies_parsed_at":"2026-05-22T04:01:34.930Z","dependency_job_id":null,"html_url":"https://github.com/SiddiqSoft/rwlenvelope","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/SiddiqSoft/rwlenvelope","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SiddiqSoft%2Frwlenvelope","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SiddiqSoft%2Frwlenvelope/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SiddiqSoft%2Frwlenvelope/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SiddiqSoft%2Frwlenvelope/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SiddiqSoft","download_url":"https://codeload.github.com/SiddiqSoft/rwlenvelope/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SiddiqSoft%2Frwlenvelope/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33328617,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-21T12:23:38.849Z","status":"online","status_checked_at":"2026-05-22T02:00:06.671Z","response_time":265,"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":["cpp17","lock","locking","nuget","reader-writer","thread-safe","utility-class","utility-classes"],"created_at":"2024-11-12T01:04:39.910Z","updated_at":"2026-05-22T04:01:56.522Z","avatar_url":"https://github.com/SiddiqSoft.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"RWLEnvelope : A simple read-writer lock envelope\n-------------------------------------------\n\n[![CodeQL](https://github.com/SiddiqSoft/RWLEnvelope/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/SiddiqSoft/RWLEnvelope/actions/workflows/codeql-analysis.yml)\n[![Build Status](https://dev.azure.com/siddiqsoft/siddiqsoft/_apis/build/status/SiddiqSoft.rwlenvelope?branchName=main)](https://dev.azure.com/siddiqsoft/siddiqsoft/_build/latest?definitionId=7\u0026branchName=main)\n![](https://img.shields.io/nuget/v/SiddiqSoft.RWLEnvelope)\n![](https://img.shields.io/github/v/tag/SiddiqSoft/RWLEnvelope)\n![](https://img.shields.io/azure-devops/tests/siddiqsoft/siddiqsoft/7)\n![](https://img.shields.io/azure-devops/coverage/siddiqsoft/siddiqsoft/7)\n\n## Objective\n\n**RWLEnvelope** is a header-only C++ template library that provides a simple, convenient envelope-access model for thread-safe access to objects using reader-writer locks. Our goal is to:\n\n- **Avoid re-implementing the rw-lock**: The standard C++ library (since C++14) provides excellent reader-writer lock implementations via [`std::shared_mutex`](https://en.cppreference.com/w/cpp/thread/shared_mutex), [`std::unique_lock`](https://en.cppreference.com/w/cpp/thread/unique_lock), and [`std::shared_lock`](https://en.cppreference.com/w/cpp/thread/shared_lock).\n\n- **Simplify thread-safe access patterns**: We provide a convenient layer that makes it easy to work with shared data in multi-threaded applications without exposing the complexity of manual lock management.\n\n- **Enable safe concurrent access**: Support multiple concurrent readers while ensuring exclusive access for writers, with automatic lock management and RAII semantics.\n\n- **Minimize boilerplate code**: Reduce the amount of locking code needed to safely access shared objects through intuitive APIs.\n\n\u003cp align=\"right\" width=\"50%\"\u003e\n\u003cb\u003eWE DO NOT IMPLEMENT\u003c/b\u003e a read-writer lock; the standard C++ library has one.\u003cbr/\u003eWe provide a header-only package simplifying the locking code around thread-safe access to your underlying type.\n\u003cbr/\u003e\n\u003ci\u003eNOT a wrapper; an envelope.\u003c/i\u003e\n\u003c/p\u003e\n\n## Why RWLEnvelope?\n\n### The Problem with Manual Lock Management\n\nWriting thread-safe code is hard. Without RWLEnvelope, you'd need to:\n\n```cpp\n// Without RWLEnvelope - verbose and error-prone\nstd::shared_mutex mutex;\nstd::map\u003cstd::string, int\u003e data;\n\n// Reading\n{\n    std::shared_lock lock(mutex);\n    auto it = data.find(\"key\");\n    if (it != data.end()) {\n        std::cout \u003c\u003c it-\u003esecond \u003c\u003c std::endl;\n    }\n} // Lock released here\n\n// Writing\n{\n    std::unique_lock lock(mutex);\n    data[\"key\"] = 42;\n} // Lock released here\n```\n\n### The RWLEnvelope Solution\n\nWith RWLEnvelope, the same code becomes cleaner and safer:\n\n```cpp\n// With RWLEnvelope - clean and safe\nsiddiqsoft::RWLEnvelope\u003cstd::map\u003cstd::string, int\u003e\u003e data;\n\n// Reading\ndata.observe\u003cvoid\u003e([](const auto\u0026 m) {\n    auto it = m.find(\"key\");\n    if (it != m.end()) {\n        std::cout \u003c\u003c it-\u003esecond \u003c\u003c std::endl;\n    }\n});\n\n// Writing\ndata.mutate\u003cvoid\u003e([](auto\u0026 m) {\n    m[\"key\"] = 42;\n});\n```\n\n### Key Benefits\n\n1. **Automatic Lock Management**: Locks are acquired and released automatically via RAII. No risk of forgetting to unlock.\n\n2. **Clear Intent**: `observe()` for reads and `mutate()` for writes makes your code's intent explicit and self-documenting.\n\n3. **Reduced Boilerplate**: No need to manually create lock objects or manage scopes. The library handles it.\n\n4. **Type Safety**: The template enforces that you're working with the correct type. No accidental type mismatches.\n\n5. **Exception Safe**: If your callback throws, the lock is still released properly. No deadlocks or resource leaks.\n\n6. **Flexible Access Patterns**: Choose between callback-based access (for simple operations) or direct lock access (for complex operations).\n\n7. **Zero Overhead**: Header-only implementation with no runtime overhead beyond the standard library's mutex.\n\n8. **Works with Any Type**: Not limited to JSON or maps. Works with any type that supports move semantics.\n\n### Real-World Scenarios\n\n**Configuration Management**:\n```cpp\nsiddiqsoft::RWLEnvelope\u003cAppConfig\u003e config;\n\n// Multiple threads reading config\nconfig.observe\u003cstd::string\u003e([](const auto\u0026 cfg) {\n    return cfg.getDatabaseUrl();\n});\n\n// Single thread updating config\nconfig.mutate\u003cvoid\u003e([](auto\u0026 cfg) {\n    cfg.setDatabaseUrl(\"new_url\");\n});\n```\n\n**Cache Implementation**:\n```cpp\nsiddiqsoft::RWLEnvelope\u003cstd::unordered_map\u003cstd::string, CacheEntry\u003e\u003e cache;\n\n// Fast concurrent reads\ncache.observe\u003cCacheEntry\u003e([](const auto\u0026 c) {\n    return c.at(\"key\");\n});\n\n// Exclusive writes\ncache.mutate\u003cvoid\u003e([](auto\u0026 c) {\n    c[\"key\"] = computeValue();\n});\n```\n\n**Shared State in Services**:\n```cpp\nsiddiqsoft::RWLEnvelope\u003cServiceState\u003e state;\n\n// Multiple reader threads\nstate.observe\u003cbool\u003e([](const auto\u0026 s) {\n    return s.isHealthy();\n});\n\n// Single writer thread\nstate.mutate\u003cvoid\u003e([](auto\u0026 s) {\n    s.updateMetrics();\n});\n```\n\n# API Documentation\n\nFor comprehensive API documentation, including detailed descriptions of all methods, usage patterns, and examples, see [**API.md**](API.md).\n\n# Requirements\n- You must be able to use [`\u003cshared_mutex\u003e`](https://en.cppreference.com/w/cpp/thread/shared_mutex) and [`\u003cmutex\u003e`](https://en.cppreference.com/w/cpp/thread/mutex).\n- Minimal target is `C++17`.\n- The build and tests are for Visual Studio 2019 under x64.\n- We use [`nlohmann::json`](https://github.com/nlohmann/json) only in our tests and the library is aware to provide a conversion operator if library is detected.\n\n# Usage\n\n- Use the nuget [SiddiqSoft.RWLEnvelope](https://www.nuget.org/packages/SiddiqSoft.RWLEnvelope/)\n- Copy paste..whatever works.\n- The idea is to not \"wrap\" the underlying type forcing you to either inherit or re-implement the types but to take advantage of the underlying type's interface whilst ensuring that we have the necessary locks.\n- Two methods:\n  - Observer/mutator model with callback and custom return to limit access and to focus the where and how to access the underlying type.\n  - Take advantage of [init-statement in if-statement](https://en.cppreference.com/w/cpp/language/if) to get the contained object within a lock and have the compiler auto-release once we leave scope.\n- A sample implementation (say you want a std::map with reader-writer lock)\n  - `using RWLMap = siddiqsoft::RWLEnvelope\u003cstd::map\u003e;`\n\n\n```cpp\n#include \"gtest/gtest.h\"\n#include \"nlohmann/json.hpp\"\n#include \"siddiqsoft/RWLEnvelope.hpp\"\n\n\nTEST(examples, AssignWithCallbacks)\n{\n\tsiddiqsoft::RWLEnvelope\u003cnlohmann::json\u003e docl; // we will assign later\n\tnlohmann::json                          doc2 {{\"baa\", 0x0baa}, {\"fee\", 0x0fee}, {\"bee\", 0x0bee}};\n\n\t// Move assign here post init\n\tdocl.reassign(std::move(doc2));\n\t// Must be empty since we moved it into the envelope\n\tEXPECT_TRUE(doc2.empty());\n\n\t// Check we have pre-change value.. Note that here we return a boolean to avoid data copy\n\tEXPECT_TRUE(docl.observe\u003cbool\u003e([](const auto\u0026 doc) -\u003e bool {\n\t\treturn (doc.value(\"fee\", 0xfa17) == 0x0fee) \u0026\u0026 (doc.value(\"baa\", 0xfa17) == 0x0baa) \u0026\u0026 (doc.value(\"bee\", 0xfa17) == 0x0bee);\n\t}));\n\n\tEXPECT_EQ(3, docl.observe\u003csize_t\u003e([](const auto\u0026 doc) { return doc.size(); }));\n}\n\n\nTEST(examples, AssignWithDirectLocks)\n{\n\tsiddiqsoft::RWLEnvelope\u003cnlohmann::json\u003e docl({{\"foo\", \"bar\"}, {\"few\", \"lar\"}});\n\tnlohmann::json                          doc2 {{\"baa\", 0x0baa}, {\"fee\", 0x0fee}, {\"bee\", 0x0bee}};\n\n\t// Previous document has two items..\n\tif (auto const\u0026 [doc, rl] = docl.readLock(); rl) { EXPECT_EQ(2, doc.size()); }\n\n\t// Modify the item (replace the initial with new)\n\tif (auto [doc, wl] = docl.writeLock(); wl) { doc = std::move(doc2); };\n\n\t//doc2 -\u003e Must be empty since we moved it into the envelope\n\tEXPECT_TRUE(doc2.empty());\n\n\t// Check we have post-change value..\n\tif (const auto\u0026 [doc, rl] = docl.readLock(); rl) { EXPECT_EQ(3, doc.size()); }\n}\n```\n\nAdditional [examples](tests/examples.cpp).\n\n# Test Coverage\n\nThe library includes comprehensive test coverage across multiple categories:\n\n## Basic Functionality Tests\n\n- **Simple Operations**: Basic envelope creation and mutation\n- **Callback-Based Access**: Testing `observe()` and `mutate()` methods with various return types\n- **Direct Lock Access**: Testing `readLock()` and `writeLock()` with structured bindings\n- **Reassignment**: Testing `reassign()` method for replacing envelope contents\n- **Snapshot Operations**: Testing `snapshot()` for independent copies\n- **Move Semantics**: Testing move constructors and move assignment\n\n## Edge Case Tests\n\n- **Default Construction**: Envelopes with default-constructed objects\n- **Return Value Forwarding**: Callbacks returning various types (void, int, string, bool, size_t)\n- **Non-JSON Types**: Testing with `std::vector\u003cint\u003e`, `std::string`, and other types\n- **Move Constructor Behavior**: Verifying source envelope state after move operations\n- **RWA Counter Tracking**: Validating the read-write-action counter accuracy\n- **Exception Safety**: Testing behavior when callbacks throw exceptions\n- **Independent Snapshots**: Verifying snapshots are truly independent copies\n- **Multiple Reassignments**: Testing repeated reassignment operations\n\n## Concurrency \u0026 Stress Tests\n\n### Reader-Writer Contention\n- **Two-Thread Tests**: Concurrent readers and writers with callbacks and direct locks\n- **Monotonic Counter Integrity**: Verifying counter never goes backwards under concurrent access\n- **Snapshot Consistency**: Ensuring snapshots return internally consistent state\n- **Concurrent Reassign**: Testing reassign racing with observe and snapshot operations\n\n### High-Contention Scenarios\n- **Zero-Sleep Maximum Contention**: All threads hammer the lock without delays\n- **Mixed API Concurrency**: All 5 API methods used concurrently on the same envelope\n- **Shared Read Lock Concurrency**: Multiple readers accessing simultaneously without blocking\n- **Concurrent Observe with Return**: Readers returning values under write contention\n\n### Data Integrity Verification\n- **RWA Counter Accuracy**: Verifying mutation counter matches exact mutate() count\n- **Paired Field Consistency**: Ensuring related fields remain synchronized\n- **Version-Data Pairing**: Validating version and data fields stay in sync during reassignment\n\n## Test Statistics\n\n- **Total Test Cases**: 20+ comprehensive test cases\n- **Concurrency Levels**: Tests with up to 16 concurrent reader threads and 8 writer threads\n- **Iteration Counts**: Stress tests with 5,000-10,000 iterations per thread\n- **Coverage Areas**:\n  - ✅ All public API methods\n  - ✅ Thread safety guarantees\n  - ✅ Lock semantics (shared vs. exclusive)\n  - ✅ Exception safety\n  - ✅ Move semantics\n  - ✅ Data consistency under contention\n  - ✅ Return value forwarding\n  - ✅ JSON serialization (when available)\n\n## Running Tests\n\nTests are built using Google Test (gtest) and can be run via the CMake build system:\n\n```bash\ncmake --preset Apple-Debug\ncmake --build --preset Apple-Debug\nctest --preset Apple-Debug\n```\n\nCoverage reports are generated and tracked via Azure Pipelines CI/CD.\n\nFor detailed test results and analysis, see [**TEST_RESULTS.md**](TEST_RESULTS.md).\n\n# Test Quality \u0026 Reliability\n\n## Comprehensive Test Suite\n\nWe take testing seriously. The library includes **38 comprehensive tests** covering:\n\n- ✅ **4 Example tests** - Real-world usage patterns\n- ✅ **4 Critical race condition tests** - Writer-writer contention, deadlock prevention, reassign-mutate racing\n- ✅ **3 High-priority race condition tests** - Exception safety, API equivalence verification\n- ✅ **2 Medium-priority race condition tests** - Snapshot isolation, RWA counter accuracy\n- ✅ **3 Low-priority race condition tests** - Memory visibility, variable lock durations\n- ✅ **4 Basic concurrency tests** - Multi-threaded reader-writer scenarios\n- ✅ **10 Edge case tests** - Exception handling, move semantics, type flexibility\n- ✅ **8 Stress tests** - High-contention scenarios with 5,000-10,000 iterations\n\n## Test Execution\n\nAll 38 tests pass successfully in ~7.6 seconds:\n- **Zero failures** - 100% pass rate\n- **No race conditions detected** - Verified with up to 16 concurrent threads\n- **Exception safe** - Callbacks throwing exceptions don't corrupt state\n- **Deadlock-free** - Explicit timeout-based deadlock detection\n- **Memory safe** - All writes visible to readers, no torn reads\n\n## Stress Testing\n\nThe test suite includes aggressive stress tests:\n- **Maximum contention**: All threads hammer the lock without delays\n- **Mixed API usage**: All 5 API methods used concurrently\n- **Variable lock durations**: Realistic lock hold times\n- **Concurrent exceptions**: Exception safety under contention\n- **Rapid reassignments**: Multiple threads reassigning simultaneously\n\n# Feedback \u0026 Contributions\n\n## Report Issues or Suggest Improvements\n\nWe want to hear about your usage scenarios! If you encounter:\n\n- **Edge cases** not covered by our tests\n- **Performance issues** in your specific use case\n- **Compatibility problems** with your type or compiler\n- **Feature requests** for additional functionality\n- **Documentation gaps** or unclear explanations\n\n**Please open an issue** on [GitHub](https://github.com/SiddiqSoft/RWLEnvelope/issues) with:\n\n1. **Your use case**: How are you using RWLEnvelope?\n2. **The scenario**: What specific situation triggered the issue?\n3. **Expected behavior**: What should happen?\n4. **Actual behavior**: What actually happened?\n5. **Reproduction steps**: How can we reproduce it?\n6. **Environment**: Compiler, OS, C++ version\n\n## Help Us Improve\n\nYour feedback helps us:\n- Identify edge cases we haven't tested\n- Optimize for real-world usage patterns\n- Improve documentation and examples\n- Ensure the library works reliably in your scenarios\n\nEven if you don't have issues, we'd love to hear about:\n- How you're using RWLEnvelope\n- Performance characteristics in your application\n- Types you're enveloping\n- Patterns that work well for you\n\n## Contributing\n\nContributions are welcome! Whether it's:\n- Additional test cases for your scenarios\n- Performance optimizations\n- Documentation improvements\n- Bug fixes\n\nPlease submit a pull request or open an issue to discuss your ideas.\n\n\u003csmall align=\"right\"\u003e\n\n\u0026copy; 2021 Siddiq Software LLC. All rights reserved.\n\n\u003c/small\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsiddiqsoft%2Frwlenvelope","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsiddiqsoft%2Frwlenvelope","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsiddiqsoft%2Frwlenvelope/lists"}