Skip to content

Commit 4e11ecd

Browse files
github-actions[bot]skyzh
authored andcommitted
vendor readerwriterqueue
1 parent be60487 commit 4e11ecd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+6996
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
*.ipch
2+
*.suo
3+
*.user
4+
*.sdf
5+
*.opensdf
6+
*.exe
7+
*.VC.db
8+
.vs/
9+
tests/stabtest/msvc*/Debug/
10+
tests/stabtest/msvc*/Release/
11+
tests/stabtest/msvc*/obj/
12+
tests/stabtest/msvc*/log.txt
13+
tests/stabtest/log.txt
14+
tests/unittests/msvc*/Debug/
15+
tests/unittests/msvc*/Release/
16+
tests/unittests/msvc*/obj/
17+
tests/CDSChecker/model-checker/
18+
benchmarks/msvc*/Debug/
19+
benchmarks/msvc*/Release/
20+
benchmarks/msvc*/obj/
21+
test/
22+
# Linux binaries
23+
benchmarks/benchmarks
24+
tests/stabtest/stabtest
25+
tests/unittests/unittests
26+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
cmake_minimum_required(VERSION 3.9)
2+
project(readerwriterqueue VERSION 1.0.0)
3+
4+
include(GNUInstallDirs)
5+
6+
add_library(${PROJECT_NAME} INTERFACE)
7+
8+
target_include_directories(readerwriterqueue INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
9+
10+
install(FILES atomicops.h readerwriterqueue.h readerwritercircularbuffer.h LICENSE.md
11+
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
This license applies to all the code in this repository except that written by third
2+
parties, namely the files in benchmarks/ext, which have their own licenses, and Jeff
3+
Preshing's semaphore implementation (used in the blocking queues) which has a zlib
4+
license (embedded in atomicops.h).
5+
6+
Simplified BSD License:
7+
8+
Copyright (c) 2013-2021, Cameron Desrochers
9+
All rights reserved.
10+
11+
Redistribution and use in source and binary forms, with or without modification,
12+
are permitted provided that the following conditions are met:
13+
14+
- Redistributions of source code must retain the above copyright notice, this list of
15+
conditions and the following disclaimer.
16+
- Redistributions in binary form must reproduce the above copyright notice, this list of
17+
conditions and the following disclaimer in the documentation and/or other materials
18+
provided with the distribution.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23+
THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
25+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27+
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
2+
# A single-producer, single-consumer lock-free queue for C++
3+
4+
This mini-repository has my very own implementation of a lock-free queue (that I designed from scratch) for C++.
5+
6+
It only supports a two-thread use case (one consuming, and one producing). The threads can't switch roles, though
7+
you could use this queue completely from a single thread if you wish (but that would sort of defeat the purpose!).
8+
9+
Note: If you need a general-purpose multi-producer, multi-consumer lock free queue, I have [one of those too][mpmc].
10+
11+
This repository also includes a [circular-buffer SPSC queue][circular] which supports blocking on enqueue as well as dequeue.
12+
13+
14+
## Features
15+
16+
- [Blazing fast][benchmarks]
17+
- Compatible with C++11 (supports moving objects instead of making copies)
18+
- Fully generic (templated container of any type) -- just like `std::queue`, you never need to allocate memory for elements yourself
19+
(which saves you the hassle of writing a lock-free memory manager to hold the elements you're queueing)
20+
- Allocates memory up front, in contiguous blocks
21+
- Provides a `try_enqueue` method which is guaranteed never to allocate memory (the queue starts with an initial capacity)
22+
- Also provides an `enqueue` method which can dynamically grow the size of the queue as needed
23+
- Also provides `try_emplace`/`emplace` convenience methods
24+
- Has a blocking version with `wait_dequeue`
25+
- Completely "wait-free" (no compare-and-swap loop). Enqueue and dequeue are always O(1) (not counting memory allocation)
26+
- On x86, the memory barriers compile down to no-ops, meaning enqueue and dequeue are just a simple series of loads and stores (and branches)
27+
28+
29+
## Use
30+
31+
Simply drop the readerwriterqueue.h (or readerwritercircularbuffer.h) and atomicops.h files into your source code and include them :-)
32+
A modern compiler is required (MSVC2010+, GCC 4.7+, ICC 13+, or any C++11 compliant compiler should work).
33+
34+
Note: If you're using GCC, you really do need GCC 4.7 or above -- [4.6 has a bug][gcc46bug] that prevents the atomic fence primitives
35+
from working correctly.
36+
37+
Example:
38+
39+
```cpp
40+
using namespace moodycamel;
41+
42+
ReaderWriterQueue<int> q(100); // Reserve space for at least 100 elements up front
43+
44+
q.enqueue(17); // Will allocate memory if the queue is full
45+
bool succeeded = q.try_enqueue(18); // Will only succeed if the queue has an empty slot (never allocates)
46+
assert(succeeded);
47+
48+
int number;
49+
succeeded = q.try_dequeue(number); // Returns false if the queue was empty
50+
51+
assert(succeeded && number == 17);
52+
53+
// You can also peek at the front item of the queue (consumer only)
54+
int* front = q.peek();
55+
assert(*front == 18);
56+
succeeded = q.try_dequeue(number);
57+
assert(succeeded && number == 18);
58+
front = q.peek();
59+
assert(front == nullptr); // Returns nullptr if the queue was empty
60+
```
61+
62+
The blocking version has the exact same API, with the addition of `wait_dequeue` and
63+
`wait_dequeue_timed` methods:
64+
65+
```cpp
66+
BlockingReaderWriterQueue<int> q;
67+
68+
std::thread reader([&]() {
69+
int item;
70+
#if 1
71+
for (int i = 0; i != 100; ++i) {
72+
// Fully-blocking:
73+
q.wait_dequeue(item);
74+
}
75+
#else
76+
for (int i = 0; i != 100; ) {
77+
// Blocking with timeout
78+
if (q.wait_dequeue_timed(item, std::chrono::milliseconds(5)))
79+
++i;
80+
}
81+
#endif
82+
});
83+
std::thread writer([&]() {
84+
for (int i = 0; i != 100; ++i) {
85+
q.enqueue(i);
86+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
87+
}
88+
});
89+
writer.join();
90+
reader.join();
91+
92+
assert(q.size_approx() == 0);
93+
```
94+
95+
Note that `wait_dequeue` will block indefinitely while the queue is empty; this
96+
means care must be taken to only call `wait_dequeue` if you're sure another element
97+
will come along eventually, or if the queue has a static lifetime. This is because
98+
destroying the queue while a thread is waiting on it will invoke undefined behaviour.
99+
100+
The blocking circular buffer has a fixed number of slots, but is otherwise quite similar to
101+
use:
102+
103+
```cpp
104+
BlockingReaderWriterCircularBuffer<int> q(1024); // pass initial capacity
105+
106+
q.try_enqueue(1);
107+
int number;
108+
q.try_dequeue(number);
109+
assert(number == 1);
110+
111+
q.wait_enqueue(123);
112+
q.wait_dequeue(number);
113+
assert(number == 123);
114+
115+
q.wait_dequeue_timed(number, std::chrono::milliseconds(10));
116+
```
117+
118+
119+
## CMake
120+
### Using targets in your project
121+
Using this project as a part of an existing CMake project is easy.
122+
123+
In your CMakeLists.txt:
124+
```
125+
include(FetchContent)
126+
127+
FetchContent_Declare(
128+
readerwriterqueue
129+
GIT_REPOSITORY https://github.com/cameron314/readerwriterqueue
130+
GIT_TAG master
131+
)
132+
133+
FetchContent_MakeAvailable(readerwriterqueue)
134+
135+
add_library(my_target main.cpp)
136+
target_link_libraries(my_target PUBLIC readerwriterqueue)
137+
```
138+
139+
In main.cpp:
140+
```cpp
141+
#include <readerwriterqueue.h>
142+
143+
int main()
144+
{
145+
moodycamel::ReaderWriterQueue<int> q(100);
146+
}
147+
```
148+
149+
### Installing into system directories
150+
As an alternative to including the source files in your project directly,
151+
you can use CMake to install the library in your system's include directory:
152+
153+
```
154+
mkdir build
155+
cd build
156+
cmake ..
157+
make install
158+
```
159+
160+
Then, you can include it from your source code:
161+
```
162+
#include <readerwriterqueue/readerwriterqueue.h>
163+
```
164+
165+
## Disclaimers
166+
167+
The queue should only be used on platforms where aligned integer and pointer access is atomic; fortunately, that
168+
includes all modern processors (e.g. x86/x86-64, ARM, and PowerPC). *Not* for use with a DEC Alpha processor (which has very weak memory ordering) :-)
169+
170+
Note that it's only been tested on x86(-64); if someone has access to other processors I'd love to run some tests on
171+
anything that's not x86-based.
172+
173+
## More info
174+
175+
See the [LICENSE.md][license] file for the license (simplified BSD).
176+
177+
My [blog post][blog] introduces the context that led to this code, and may be of interest if you're curious
178+
about lock-free programming.
179+
180+
181+
[blog]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++
182+
[license]: LICENSE.md
183+
[benchmarks]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++#benchmarks
184+
[gcc46bug]: http://stackoverflow.com/questions/16429669/stdatomic-thread-fence-has-undefined-reference
185+
[mpmc]: https://github.com/cameron314/concurrentqueue
186+
[circular]: readerwritercircularbuffer.h

0 commit comments

Comments
 (0)