{"id":26474175,"url":"https://github.com/mrizaln/doublebuf","last_synced_at":"2025-06-11T00:35:11.141Z","repository":{"id":254597652,"uuid":"846989102","full_name":"mrizaln/doublebuf","owner":"mrizaln","description":"A simple lock-free double buffering mechanism implementation written in C++20","archived":false,"fork":false,"pushed_at":"2025-05-23T07:42:53.000Z","size":31,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-23T09:14:15.013Z","etag":null,"topics":["cpp","double-buffer","lock-free"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mrizaln.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2024-08-24T14:30:30.000Z","updated_at":"2025-05-23T07:42:33.000Z","dependencies_parsed_at":"2025-04-18T19:47:22.971Z","dependency_job_id":"e8e6b955-db0a-473b-a495-a2fe4aad6d05","html_url":"https://github.com/mrizaln/doublebuf","commit_stats":null,"previous_names":["mrizaln/doublebuf"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrizaln%2Fdoublebuf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrizaln%2Fdoublebuf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrizaln%2Fdoublebuf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrizaln%2Fdoublebuf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrizaln","download_url":"https://codeload.github.com/mrizaln/doublebuf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrizaln%2Fdoublebuf/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259176376,"owners_count":22817182,"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","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","double-buffer","lock-free"],"created_at":"2025-03-19T22:45:20.184Z","updated_at":"2025-06-11T00:35:11.126Z","avatar_url":"https://github.com/mrizaln.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# doublebuf\n\nA simple lock-free double buffering mechanism implementation written in C++20\n\n## Features\n\nThis library provides two double buffering mechanism,\n\n- `DoubleBuf`\n\n  Standard double buffering mechanism.\n\n- `LazyDoubleBuf`\n\n  Lazily updated double buffering mechanism. This buffering is akin to triple buffering where there will be fewer updates done into the back buffer: the update to the back buffer is only done if the data is swapped by the consumer. The difference with triple buffering then is the number of buffer used, with this one, there is only two buffers.\n\n  You can think of this buffering method as triple buffering with its back buffer can be said as the middle buffer of a traditional triple buffering mechanism. Your data that is used to update the back buffer of `LazyDoubleBuf` then is the back buffer of a traditional triple buffering mechanism.\n\n  The recency of this buffer is then as bad as a triple buffering and affected by the rate of the consumer swapping the buffer.\n\n  ```\n                                    ╭─────────────────────────────────────────╮\n                                    │              LazyDoubleBuf              │\n                                    ├─────────────────────────────────────────┤\n  ┏━━━━━━━━━━━┓    `update(fn)`     │  ┌─────────────┐       ┌──────────────┐ │\n  ┃ Producer  ┃───────────────────────▶│ back_buffer │◀─────▶│ front_buffer │ │\n  ┗━━━━━━━━━━━┛ │ only called if  │ │  └─────────────┘   ▲   └──────────────┘ │\n                │swap has happened│ ╰────────────────────│────────────────────╯\n                                                         │\n                                                `swap()` │ │swap might not happen│\n                                                         │ │if data is not ready │\n                                                         │\n                                                    ┏━━━━━━━━━━┓\n                                                    ┃ Consumer ┃\n                                                    ┗━━━━━━━━━━┛\n  ```\n## Usage\n\n\u003e `CMakeLists.txt`\n\n```cmake\ncmake_minimum_required(VERSION 3.14)\n\ninclude(FetchContent)\n\nFetchContent_Declare(\n  doublebuf\n  GIT_REPOSITORY https://github.com/mrizaln/doublebuf\n  GIT_TAG v0.2.0)\nFetchContent_MakeAvailable(doublebuf)\n\nadd_executable(main main.cpp)\ntarget_link_libraries(main PRIVATE doublebuf)\n```\n\n## Example\n\n\u003e main.cpp\n\n```cpp\n#include \u003cdoublebuf/double_buffer_atomic.hpp\u003e\n\n#include \u003cchrono\u003e\n#include \u003cformat\u003e\n#include \u003ciostream\u003e\n#include \u003cthread\u003e\n\nusing doublebuf::LazyDoubleBuf;\n\ntemplate \u003ctypename... Ts\u003e\nvoid println(std::format_string\u003cTs...\u003e\u0026\u0026 fmt, Ts\u0026\u0026... ts)\n{\n    auto string = std::format(fmt, std::forward\u003cTs\u003e(ts)...);\n    std::cout \u003c\u003c std::format(\"{}\\n\", string) \u003c\u003c std::flush;\n}\n\nint main()\n{\n    using namespace std::chrono_literals;\n\n    auto buf = LazyDoubleBuf\u003cstd::string\u003e{ \"front\", \"back\" };\n\n    // producer thread\n    std::jthread producer([\u0026buf](const std::stop_token\u0026 st) {\n        int counter = 0;\n        while (!st.stop_requested()) {\n\n            /* some work... */\n\n            ++counter;\n            println(\"producer: counter: {}\", counter);\n\n            // will be called called only if the buffer is idle (after swap)\n            auto update = buf.update_buffers([\u0026counter](std::string\u0026 buffer) {\n                buffer = std::format(\"{0} ==\u003e {0:032b}\", counter);\n                println(\"producer: [U] buffer: {}\", buffer);\n            });\n\n            if (update) {\n                // do some logic if data sent to back buffer (lambda called)\n            } else {\n                // do some logic if data is not sent to back buffer (lambda ignored)\n            }\n\n            std::this_thread::sleep_for(87ms);\n        }\n    });\n\n    // consumer thread (this thread)\n\n    using Clock = std::chrono::steady_clock;\n\n    auto duration = 10s;\n    auto start    = Clock::now();\n\n    while (Clock::now() \u003c start + duration) {\n\n        /* some work... */\n\n        // front buffer guaranteed to be free to use after this until the next call to swap_buffers()\n        auto\u0026\u0026 [buffer, swapped] = buf.swap_buffers();\n\n        // swap_buffers() may not swap anything and return false flag to indicate that\n        if (not swapped) {\n            // do something\n        }\n\n        /* using the front buffer */\n\n        println(\"consumer: (S) buffer: {}\", buffer);\n\n        std::this_thread::sleep_for(1s);\n    };\n\n    println(\"{} has passed, time to stop...\", duration);\n\n    producer.request_stop();\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrizaln%2Fdoublebuf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrizaln%2Fdoublebuf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrizaln%2Fdoublebuf/lists"}