{"id":18021798,"url":"https://github.com/dnedic/lfbb","last_synced_at":"2026-02-25T00:04:23.338Z","repository":{"id":57807125,"uuid":"527223257","full_name":"DNedic/lfbb","owner":"DNedic","description":"A Lock Free Bipartite Buffer Library written in standard C11","archived":false,"fork":false,"pushed_at":"2024-05-08T20:29:13.000Z","size":469,"stargazers_count":71,"open_issues_count":0,"forks_count":10,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-05-29T12:11:01.242Z","etag":null,"topics":["buffer","c","c11","circular-buffer","cmake","dma","embedded","embedded-systems","fifo","library","lock-free","ring-buffer"],"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/DNedic.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2022-08-21T14:01:37.000Z","updated_at":"2025-04-11T00:48:44.000Z","dependencies_parsed_at":"2024-01-04T12:31:18.273Z","dependency_job_id":"62b93f2e-45d3-4169-ac00-2042390fbbae","html_url":"https://github.com/DNedic/lfbb","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/DNedic/lfbb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DNedic%2Flfbb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DNedic%2Flfbb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DNedic%2Flfbb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DNedic%2Flfbb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DNedic","download_url":"https://codeload.github.com/DNedic/lfbb/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DNedic%2Flfbb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29806144,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T22:43:48.403Z","status":"ssl_error","status_checked_at":"2026-02-24T22:43:18.536Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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":["buffer","c","c11","circular-buffer","cmake","dma","embedded","embedded-systems","fifo","library","lock-free","ring-buffer"],"created_at":"2024-10-30T06:11:13.362Z","updated_at":"2026-02-25T00:04:23.317Z","avatar_url":"https://github.com/DNedic.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LFBB - Lock Free Bipartite Buffer\n![CMake](https://github.com/DNedic/lfbb/actions/workflows/.github/workflows/cmake.yml/badge.svg)\n\nLFBB is a bipartite buffer implementation written in standard C11, suitable for all platforms, from deeply embedded to HPC uses. It is lock-free for single consumer single producer scenarios making it incredibly performant and easy to use.\n\n## What is a bipartite buffer\n\nA bipartite buffer is a variation of the classic [ring buffer](https://en.wikipedia.org/wiki/Circular_buffer) with the ability to always be able to provide the user with contiguous memory regions for writing/reading if there is enough space/data.\n\n## Why use a bipartite buffer\nA bipartite buffer should be used everywhere a ring buffer is used if you want:\n* To offload transfers to DMA increasing the transfer speed and freeing up CPU time\n* To avoid creating intermediate buffers for APIs that require contiguous data\n* To process data inside the buffer without dequeueing it\n* For scenarios where operations on data might fail or only some data might be used\n\n## Features\n* Written in standard C11, compatible with all platforms supporting it\n* Lock free thread and multicore safe in single producer single consumer scenarios\n* No dynamic allocation\n* Optimized for high performance\n* MIT Licensed\n\n## How to get\nThere are three main ways to get the library:\n* Using CMake [FetchContent()](https://cmake.org/cmake/help/latest/module/FetchContent.html)\n* As a [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules)\n* By downloading a release from GitHub\n\n## How to use\nShown here is an example of typical use:\n* Consumer thread/interrupt\n```c\nsize_t data_available;\nuint8_t *read_ptr = LFBB_ReadAcquire(\u0026lfbb_adc, \u0026data_available);\n\nif (read_ptr != NULL) {\n    size_t data_used = DoStuffWithData(read_ptr, data_available);\n    LFBB_ReadRelease(\u0026lfbb_adc, data_used);\n}\n```\n\n* Producer thread/interrupt\n```c\nif (!write_started) {\n    uint8_t *write_ptr = LFBB_WriteAcquire(\u0026lfbb_adc, sizeof(data));\n    if (write_ptr != NULL) {\n        ADC_StartDma(\u0026adc_dma_h, write_ptr, sizeof(data));\n        write_started = true;\n    }\n} else {\n    if (ADC_PollDmaComplete(\u0026adc_dma_h) {\n        LFBB_WriteRelease(\u0026lfbb_adc, sizeof(data));\n        write_started = false;\n    }\n}\n```\n\n## Configuration\nThe library offers two configuration defines ```LFBB_MULTICORE_HOSTED``` and ```LFBB_CACHELINE_LENGTH``` that can be passed by the build system or defined before including the library if the configuration isn't suitable.\n\nOn embedded systems it is usually required to do manual cache synchronization, so ```LFBB_MULTICORE_HOSTED``` should be left as ```false``` to avoid wasting space on padding for cacheline alignment of indexes.\n\nFor hosted systems the [False Sharing](https://en.wikipedia.org/wiki/False_sharing) phenomenom can reduce performance to some extent which is why passing ```LFBB_MULTICORE_HOSTED``` as ```true``` is advisable. This aligns the indexes to the system cacheline size, ```64``` by default.\n\nSome systems have a non-typical cacheline length (for instance the apple M1/M2 CPUs have a cacheline length of 128 bytes), and ```LFBB_CACHELINE_LENGTH``` should be set accordingly in those cases.\n\n## How it works\nThe Bipartite Buffer uses the same base principle as the [ring buffer data structure](https://en.wikipedia.org/wiki/Circular_buffer), however its ability to provide contiguous space for writing and reading requires modifying the approach slightly.\n\nLet's consider a typical usage scenario, we want to acquire 4 slots for writing in the following buffer:\n\n\u003cp align=\"center\" width=\"100%\"\u003e\n    \u003cimg src=\"docs/images/ring_buf_has_space.svg\" width=40%\u003e\n\u003c/p\u003e\n\nWe have 7 free slots, so this would work fine for a regular ring buffer.\nHowever when we unroll the buffer we can notice the issue:\n\n\u003cp align=\"center\" width=\"100%\"\u003e\n    \u003cimg src=\"docs/images/ring_buf_unwrapped_has_space.svg\" width=80%\u003e\n\u003c/p\u003e\n\nWe cannot acquire 4 slots from the start of the free space, as there is not enough contiguous space until the end of the buffer, and the only solution is to acquire 4 slots from the beginning.\n\nAfter acquiring those slots, we have a gap in available data caused by the skip and we must somehow tell the buffer to avoid reading from that region:\n\n\u003cp align=\"center\" width=\"100%\"\u003e\n    \u003cimg src=\"docs/images/bipartite_buf_unwrapped_after_wrapping_write.svg\" width=80%\u003e\n\u003c/p\u003e\n\nThis is where we introduce another index - the **invalidate index** ``i``.\nWe can set it to the start of the region we want to skip, and next time we are reading the data, we only consider data until the invalidate index.\n\n\u003cp align=\"center\" width=\"100%\"\u003e\n    \u003cimg src=\"docs/images/bipartite_buf_unwrapped_after_invalidate.svg\" width=80%\u003e\n\u003c/p\u003e\n\nNow the first time we acquire data for reading, we will get the region from `r` to `i`:\n\n\u003cp align=\"center\" width=\"100%\"\u003e\n    \u003cimg src=\"docs/images/bipartite_buf_unwrapped_after_invalidate_read1.svg\" width=80%\u003e\n\u003c/p\u003e\n\nAnd the next time we will acquire the region from `0` to `w`:\n\n\u003cp align=\"center\" width=\"100%\"\u003e\n    \u003cimg src=\"docs/images/bipartite_buf_unwrapped_after_invalidate_read2.svg\" width=80%\u003e\n\u003c/p\u003e\n\nLastly, when writing, we can write over invalidated parts of the buffer as it doesn't contain anything useful, but also have to move the invalidate index:\n\n\u003cp align=\"center\" width=\"100%\"\u003e\n    \u003cimg src=\"docs/images/bipartite_buf_unwrapped_after_invalidate_write.svg\" width=80%\u003e\n\u003c/p\u003e\n\nFor more details, [here](https://www.codeproject.com/Articles/3479/The-Bip-Buffer-The-Circular-Buffer-with-a-Twist) is a nice writeup about Bipartite Buffers.\n\n## Dealing with caches on embedded systems\nWhen using the library with DMA or asymmetric multicore on embedded systems with cache it is necessary to perform manual cache synchronization in one of the following ways:\n* Using platform specific data synchronization barriers (```DSB``` on ARM)\n* By manually invalidating cache\n* By setting the MPU/MMU up to not cache the data buffer\n\n## Caveats\n* The library does not implement alignment of writes and reads, it is up to the user to only write in factors they want the data to be aligned to, adequately size and align the buffer used\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdnedic%2Flfbb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdnedic%2Flfbb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdnedic%2Flfbb/lists"}