Skip to content

SiddiqSoft/rwlenvelope

Repository files navigation

RWLEnvelope : A simple read-writer lock envelope

CodeQL Build Status

Quick Start

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.

#include "siddiqsoft/RWLEnvelope.hpp"

// Declare a map as container and initialize it with some values
// Using initializer list constructor for convenient initialization.
Note the brace-count as we use brace initializer to construct the RWLEnvelope
// while constructing its internal storage with the std::map initializer!
siddiqsoft::RWLEnvelope<std::map<std::string, int>> data({
    {"key", 1010},
    {"key2", 2020},
    {"key3", 3030}
});

// Read-only access
auto val = data.observe([](const auto& m) noexcept -> auto {
    return m.value("key", 0);
});

// Read-write access
data.mutate([](auto& m) noexcept {
    m["key"] = 42;
});

std::println(std::cerr, "Contents: {}", data.snapshot());

Why RWLEnvelope?

  • Automatic Lock Management: No manual lock/unlock needed
  • Clear Intent: observe() for reads, mutate() for writes
  • Exception Safe: Locks released properly even if callbacks throw
  • Zero Overhead: Header-only, no runtime cost beyond std::shared_mutex
  • Works with Any Type: Not limited to specific containers
  • Convenient Initialization: Supports initializer lists for easy construction

Documentation

📖 Complete API Documentation

The full documentation includes:

  • Detailed API reference
  • Usage patterns and examples
  • Performance considerations
  • Thread safety guarantees
  • Requirements and limitations

Installation

NuGet Package

Install-Package SiddiqSoft.RWLEnvelope

Header-Only

Copy include/siddiqsoft/RWLEnvelope.hpp to your project.

Requirements

  • C++ Standard: C++20 or later (requires C++20 concepts support)
  • Compiler: Must support [[nodiscard]] attribute and C++20 concepts
  • Headers: <shared_mutex>, <functional>, <tuple>, <utility>, <concepts>
  • Optional: nlohmann/json library for JSON serialization support

Key Features

Multiple Concurrent Readers

// Multiple threads can read simultaneously
data.observe([](const auto& m) noexcept {
    return m.at("key");
});

Exclusive Writer

// Only one thread can write at a time
data.mutate([](auto& m) noexcept {
    m["key"] = 42;
});

Direct Lock Access

// For complex operations
if (auto [map, lock] = data.writeLock(); lock) {
    map["key"] = 42;
    map.erase("old_key");
}

Snapshot for External Processing

// Get a copy to process outside the lock
std::vector<int> copy = data.snapshot();
std::sort(copy.begin(), copy.end());

Initializer List Construction

// Construct with initializer list - works with any container supporting std::initializer_list
siddiqsoft::RWLEnvelope<std::vector<int>> vec({1, 2, 3, 4, 5});

// Maps and other associative containers
siddiqsoft::RWLEnvelope<std::map<std::string, int>> map({
    {"key1", 1},
    {"key2", 2},
    {"key3", 3}
});

// JSON objects
siddiqsoft::RWLEnvelope<nlohmann::json> doc({
    {"name", "John"},
    {"age", 30},
    {"active", true}
});

Real-World Examples

Configuration Management

siddiqsoft::RWLEnvelope<AppConfig> config;

// Multiple threads reading config
auto url = config.observe([](const auto& cfg) noexcept {
    return cfg.databaseUrl;
});

// Single thread updating config
config.mutate([](auto& cfg) noexcept {
    cfg.databaseUrl = "new_url";
});

Cache Implementation

siddiqsoft::RWLEnvelope<std::unordered_map<std::string, CacheEntry>> cache;

// Fast concurrent reads
auto val = cache.observe([](const auto& c) noexcept {
    return c.at("key").value;
});

// Exclusive writes
cache.mutate([](auto& c) noexcept {
    c["key"] = {"computed_value", now()};
});

Initializing with Data

// Initialize a cache with pre-populated data
siddiqsoft::RWLEnvelope<std::map<std::string, std::string>> cache({
    {"user:1", "Alice"},
    {"user:2", "Bob"},
    {"user:3", "Charlie"}
});

// Initialize a JSON document with structured data
siddiqsoft::RWLEnvelope<nlohmann::json> config({
    {"database", {{"host", "localhost"}, {"port", 5432}}},
    {"cache", {{"ttl", 3600}, {"enabled", true}}}
});

Testing

The library includes comprehensive test coverage with 38+ tests covering:

  • Basic functionality (observe, mutate, readLock, writeLock)
  • Initializer list construction
  • Edge cases and exception safety
  • Concurrent access patterns
  • Data integrity under contention

Run tests:

cmake --preset Apple-Debug
cmake --build --preset Apple-Debug
ctest --preset Apple-Debug

License

BSD 3-Clause License - See LICENSE file for details

Resources

© 2021 Abdulkareem Siddiq. All rights reserved.