{"id":16486099,"url":"https://github.com/wyfo/swap-buffer-queue","last_synced_at":"2025-09-19T11:31:22.609Z","repository":{"id":109498082,"uuid":"595784547","full_name":"wyfo/swap-buffer-queue","owner":"wyfo","description":"A buffering MPSC queue.","archived":false,"fork":false,"pushed_at":"2024-07-12T11:05:26.000Z","size":1463,"stargazers_count":79,"open_issues_count":2,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-01T07:39:58.339Z","etag":null,"topics":["rust"],"latest_commit_sha":null,"homepage":"https://wyfo.github.io/swap-buffer-queue/","language":"Rust","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/wyfo.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}},"created_at":"2023-01-31T20:00:44.000Z","updated_at":"2025-07-11T18:38:16.000Z","dependencies_parsed_at":"2023-11-12T22:29:04.381Z","dependency_job_id":"b1f973bd-0a2c-4c63-b966-a373bd5c8f10","html_url":"https://github.com/wyfo/swap-buffer-queue","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/wyfo/swap-buffer-queue","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyfo%2Fswap-buffer-queue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyfo%2Fswap-buffer-queue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyfo%2Fswap-buffer-queue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyfo%2Fswap-buffer-queue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wyfo","download_url":"https://codeload.github.com/wyfo/swap-buffer-queue/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyfo%2Fswap-buffer-queue/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275927880,"owners_count":25554281,"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-09-19T02:00:09.700Z","response_time":108,"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":["rust"],"created_at":"2024-10-11T13:28:20.924Z","updated_at":"2025-09-19T11:31:22.244Z","avatar_url":"https://github.com/wyfo.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# swap-buffer-queue\n\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](\nhttps://github.com/wyfo/swap-buffer-queue/blob/main/LICENSE)\n[![Cargo](https://img.shields.io/crates/v/swap-buffer-queue.svg)](\nhttps://crates.io/crates/swap-buffer-queue)\n[![Documentation](https://docs.rs/swap-buffer-queue/badge.svg)](\nhttps://docs.rs/swap-buffer-queue)\n\nA buffering MPSC queue.\n\nThis library is intended to be a (better, I hope) alternative to traditional MPSC queues in the context of a buffering consumer, by moving the buffering part directly into the queue.\n\nIt is especially well suited for IO writing workflow, see [buffer implementations](#buffer-implementations).\n\nThe crate is `no_std` – some buffer implementations may require `alloc` crate.\n\nIn addition to the low level `Queue` implementation, a higher level `SynchronizedQueue` is provided with both blocking and asynchronous methods. Synchronization feature requires `std`.\n\n\n## Example\n\n```rust\nuse std::ops::Deref;\nuse swap_buffer_queue::{buffer::{IntoValueIter, VecBuffer}, Queue};\n\n// Initialize the queue with a capacity\nlet queue: Queue\u003cVecBuffer\u003cusize\u003e\u003e = Queue::with_capacity(42);\n// Enqueue some value\nqueue.try_enqueue([0]).unwrap();\n// Multiple values can be enqueued at the same time\n// (optimized compared to multiple enqueuing)\nqueue.try_enqueue([1, 2]).unwrap();\nlet mut values = vec![3, 4];\nqueue\n    .try_enqueue(values.drain(..).into_value_iter())\n    .unwrap();\n// Dequeue a slice to the enqueued values\nlet slice = queue.try_dequeue().unwrap();\nassert_eq!(slice.deref(), \u0026[0, 1, 2, 3, 4]);\n// Enqueued values can also be retrieved\nassert_eq!(slice.into_iter().collect::\u003cVec\u003c_\u003e\u003e(), vec![0, 1, 2, 3, 4]);\n```\n\n\n## Buffer implementations\n\nIn addition to simple [`ArrayBuffer`](https://docs.rs/swap-buffer-queue/latest/swap_buffer_queue/buffer/struct.ArrayBuffer.html) and [`VecBuffer`](https://docs.rs/swap-buffer-queue/latest/swap_buffer_queue/buffer/struct.VecBuffer.html), this crate provides useful write-oriented implementations.\n\n### [`write`](https://docs.rs/swap-buffer-queue/latest/swap_buffer_queue/write/index.html)\n\n[`WriteArrayBuffer`](https://docs.rs/swap-buffer-queue/latest/swap_buffer_queue/write/struct.WriteVecBuffer.html) and \n[`WriteVecBuffer`](https://docs.rs/swap-buffer-queue/latest/swap_buffer_queue/write/struct.WriteVecBuffer.html) are well suited when there are objects to be serialized with a known-serialization size. Indeed, objects can then be serialized directly on the queue's buffer, avoiding allocation.\n\n```rust\nuse std::io::Write;\nuse swap_buffer_queue::{Queue, write::{WriteBytesSlice, WriteVecBuffer}};\n\n// Creates a WriteVecBuffer queue with a 2-bytes header\nlet queue: Queue\u003cWriteVecBuffer\u003c2\u003e\u003e = Queue::with_capacity((1 \u003c\u003c 16) - 1);\nqueue\n    .try_enqueue((256, |slice: \u0026mut [u8]| { /* write the slice */ }))\n    .unwrap();\nqueue\n    .try_enqueue((42, |slice: \u0026mut [u8]| { /* write the slice */ }))\n    .unwrap();\nlet mut slice = queue.try_dequeue().unwrap();\n// Adds a header with the len of the buffer\nlet len = (slice.len() as u16).to_be_bytes();\nslice.header().copy_from_slice(\u0026len);\n// Let's pretend we have a writer\nlet mut writer: Vec\u003cu8\u003e = Default::default();\nassert_eq!(writer.write(slice.frame()).unwrap(), 300);\n```\n\n### [`write_vectored`](https://docs.rs/swap-buffer-queue/latest/swap_buffer_queue/write/index.html)\n\n[`WriteVectoredArrayBuffer`](https://docs.rs/swap-buffer-queue/latest/swap_buffer_queue/write_vectored/struct.WriteVectoredVecBuffer.html) and\n[`WriteVectoredVecBuffer`](https://docs.rs/swap-buffer-queue/latest/swap_buffer_queue/write_vectored/struct.WriteVectoredVecBuffer.html) allows buffering a slice of [`IoSlice`](https://doc.rust-lang.org/std/io/struct.IoSlice.html), saving the cost of dequeuing io-slices one by one to collect them after.\n(Internally, two buffers are used, one of the values, and one for the io-slices)\n\nAs a convenience, total size of the buffered io-slices can be retrieved.\n\n```rust\nuse std::io::{Write};\nuse swap_buffer_queue::{Queue, write_vectored::WriteVectoredVecBuffer};\n\n// Creates a WriteVectoredVecBuffer queue\nlet queue: Queue\u003cWriteVectoredVecBuffer\u003cVec\u003cu8\u003e\u003e, Vec\u003cu8\u003e\u003e = Queue::with_capacity(100);\nqueue.try_enqueue([vec![0; 256]]).unwrap();\nqueue.try_enqueue([vec![42; 42]]).unwrap();\nlet mut total_size = 0u16.to_be_bytes();\nlet mut slice = queue.try_dequeue().unwrap();\n// Adds a header with the total size of the slices\ntotal_size.copy_from_slice(\u0026(slice.total_size() as u16).to_be_bytes());\nlet mut frame = slice.frame(.., Some(\u0026total_size), None);\n// Let's pretend we have a writer\nlet mut writer: Vec\u003cu8\u003e = Default::default();\nassert_eq!(writer.write_vectored(\u0026mut frame).unwrap(), 300);\n```\n\n## How it works \n\nInternally, this queue use 2 buffers: one being used for enqueuing while the other is dequeued. \n\nWhen [`Queue::try_enqueue`](https://docs.rs/swap-buffer-queue/latest/swap_buffer_queue/struct.Queue.html#method.try_enqueue) is called, it reserves atomically a slot in the current enqueuing buffer. The value is then inserted in the slot.\n\nWhen [`Queue::try_dequeue`](https://docs.rs/swap-buffer-queue/latest/swap_buffer_queue/struct.Queue.html#method.try_dequeue) is called, both buffers are swapped atomically, so dequeued buffer will contain previously enqueued values, and new enqueued ones will go to the other (empty) buffer. \n\nAs the two-phase enqueuing cannot be atomic, the queue can be in a transitory state, where slots have been reserved but have not been written yet. In this rare case, dequeuing will fail and have to be retried.\n\n## Fairness\n\n`SynchronizedQueue` implementation is not fair, i.e. it doesn't ensure that the oldest blocked enqueuer will succeed when the capacity becomes available.\n\nHowever, this issue is quite mitigated by the fact that all the capacity becomes available at once, so all blocked enqueuers may succeed (especially with one-sized values).\n\nFor the particular case of potential big variable-sized values, it's still possible to combine the queue with a semaphore, e.g. `tokio::sync::Semaphore`. Performance will be impacted, but the algorithm is [fast enough](#performance) to afford it.\n\nI'm still thinking about a way to include fairness directly in the algorithm, but it's not an easy thing to do.\n\n## Unsafe\n\nThis library uses unsafe code, for three reasons:\n- buffers are wrapped in `UnsafeCell` to allow mutable for the dequeued buffer;\n- buffers implementation may use unsafe to allow insertion with shared reference;\n- `Buffer` trait require unsafe interface for its invariant, because it's public.\n\nTo ensure the safety of the algorithm, it uses:\n- tests (mostly doctests for now, but it needs to be completed)\n- benchmarks\n- MIRI (with tests)\n\nLoom is partially integrated for now, but loom tests are on the TODO list.\n\n## Performance\n\nswap-buffer-queue is very performant – it's actually the fastest MPSC queue I know.\n\nHere is the crossbeam benchmark [forked](https://github.com/wyfo/crossbeam/tree/bench_sbq/crossbeam-channel/benchmarks)\n\n| benchmark     | crossbeam | swap-buffer-queue |\n|---------------|-----------|-------------------|\n| bounded1_mpsc | 1.545s    | 1.763s            |\n| bounded1_spsc | 1.652s    | 1.000s            |\n| bounded_mpsc  | 0.362s    | 0.137s            |\n| bounded_seq   | 0.190s    | 0.114s            |\n| bounded_spsc  | 0.115s    | 0.092s            |\n\nHowever, a large enough capacity is required to reach maximum performance; otherwise, high contention scenario may be penalized.\nThis is because the algorithm put all the contention on a single atomic integer (instead of two for crossbeam).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwyfo%2Fswap-buffer-queue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwyfo%2Fswap-buffer-queue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwyfo%2Fswap-buffer-queue/lists"}