@encharm/cws 4.8.4 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/CLAUDE.md +5 -1
- package/README.md +12 -0
- package/binding.gyp +4 -2
- package/deps/readerwriterqueue/LICENSE.md +28 -0
- package/deps/readerwriterqueue/README.md +186 -0
- package/deps/readerwriterqueue/atomicops.h +761 -0
- package/deps/readerwriterqueue/readerwriterqueue.h +979 -0
- package/dist/bindings/cws_darwin_arm64_node115.node +0 -0
- package/dist/bindings/cws_darwin_arm64_node127.node +0 -0
- package/dist/bindings/cws_darwin_arm64_node137.node +0 -0
- package/dist/bindings/cws_darwin_arm64_node147.node +0 -0
- package/dist/bindings/cws_linux_arm64_node115.node +0 -0
- package/dist/bindings/cws_linux_arm64_node127.node +0 -0
- package/dist/bindings/cws_linux_arm64_node137.node +0 -0
- package/dist/bindings/cws_linux_arm64_node147.node +0 -0
- package/dist/bindings/cws_linux_x64_node115.node +0 -0
- package/dist/bindings/cws_linux_x64_node127.node +0 -0
- package/dist/bindings/cws_linux_x64_node137.node +0 -0
- package/dist/bindings/cws_linux_x64_node147.node +0 -0
- package/dist/bindings/cws_win32_x64_node115.node +0 -0
- package/dist/bindings/cws_win32_x64_node127.node +0 -0
- package/dist/bindings/cws_win32_x64_node137.node +0 -0
- package/dist/bindings/cws_win32_x64_node147.node +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -0
- package/dist/shared.d.ts +1 -0
- package/dist/shared.js +2 -1
- package/package.json +1 -1
- package/src/Addon.cpp +2 -0
- package/src/Addon.h +6 -0
- package/src/Hub.cpp +4 -2
- package/src/MicroDeflate.h +109 -0
- package/src/Networking.h +5 -2
- package/src/SendWorker.cpp +229 -0
- package/src/SendWorker.h +25 -0
- package/src/Socket.h +195 -4
- package/src/WebSocket.cpp +20 -2
- package/src/WebSocket.h +1 -0
- package/src/Zlib.cpp +33 -1
- package/src/Zlib.h +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## Released 4.10.0
|
|
2
|
+
* microdeflate: a built-in ~150-line raw-DEFLATE encoder (fixed Huffman, greedy LZ77, no stream state, no per-message hash reset) now compresses independent messages, i.e. the shared-compressor mode. Measured on a real BSON RPC stream: same ratio as zlib-ng level 1 (2.84 vs 2.85) at ~1.7x its speed; a compressed 2 KB message costs 4.6 µs of worker CPU instead of 6.0. Output is standard DEFLATE (round-trip tested against zlib's inflater). `CWS_MICRO_DEFLATE=0` falls back to zlib-ng; `zlibBackend` reports `+ microdeflate` when active. Dedicated windows (context takeover) and inflate stay on zlib-ng.
|
|
3
|
+
* Compression moved to the send worker: `send()` of a compressed message queues the raw payload and the worker deflates + frames it. Main-thread cost of a compressed 2 KB RPC message drops from ~6 µs to ~0.7 µs (Linux, per-thread measurement); wire output is byte-identical. Main-thread write paths (same-tick terminate, full worker queue, drain loop after a short write) deflate pending messages themselves first.
|
|
4
|
+
|
|
5
|
+
## Released 4.9.0
|
|
6
|
+
* Send worker thread: the end-of-tick gathered writes run on a dedicated thread (lock-free SPSC hand-off via the vendored `readerwriterqueue`), taking the kernel's TCP work off the JavaScript thread. Measured on a loaded EPYC: 30-65% less main-thread CPU per message, 1.7-2.2x fan-out throughput. `CWS_SEND_THREAD=0` disables; `sendThread` export reports status. A socket closed with a send in flight keeps its fd open until that send completes (prevents fd reuse races).
|
|
7
|
+
|
|
1
8
|
## Released 4.8.4
|
|
2
9
|
* `npm install` no longer rebuilds the binding from source when a prebuilt one matches the platform and Node ABI. Since the node-gyp fallback was repaired in 4.8.0, every install on a machine with a compiler was silently replacing the shipped zlib-ng binding with a Node-zlib build. `CWS_FORCE_BUILD=1` forces the source build.
|
|
3
10
|
|
package/CLAUDE.md
CHANGED
|
@@ -25,7 +25,7 @@ Tests bind ports 3000 (ws) and 3001 (wss, certs in `tests/certs/`). The test fil
|
|
|
25
25
|
|
|
26
26
|
1. Download official Node header tarballs for one pinned version per supported major into `targets/` (`VER_115`=Node 20, `VER_127`=Node 22, `VER_137`=Node 24, `VER_147`=Node 26; the number is the Node ABI / `process.versions.modules`).
|
|
27
27
|
2. Compile `src/*.cpp` once per ABI with `g++`/`cl` directly, with `-I src/headers/$V` for the matching Node major.
|
|
28
|
-
3. Build the vendored zlib-ng (`deps/zlib-ng`, native `zng_` API) once per OS/arch: CMake into `deps/zlib-ng/build-<OS>-<arch>/` on macOS/Linux, `nmake -f win32\Makefile.msc` on Windows. The bindings are compiled with `-DCWS_ZLIB_NG` and link it statically. `src/Zlib.cpp` is the only file that includes a zlib header; without the define (node-gyp fallback) it uses Node's zlib.
|
|
28
|
+
3. Build the vendored zlib-ng (`deps/zlib-ng`, native `zng_` API) once per OS/arch: CMake into `deps/zlib-ng/build-<OS>-<arch>/` on macOS/Linux, `nmake -f win32\Makefile.msc` on Windows. The bindings are compiled with `-DCWS_ZLIB_NG` and link it statically. `src/MicroDeflate.h` is a self-contained fixed-Huffman DEFLATE encoder used for independent messages (shared mode) via `zlib::deflateIndependent`; `src/Zlib.cpp` is the only file that includes a zlib header; without the define (node-gyp fallback) it uses Node's zlib.
|
|
29
29
|
4. Emit `dist/bindings/cws_<platform>_<arch>_node<ABI>.node`.
|
|
30
30
|
|
|
31
31
|
```sh
|
|
@@ -72,6 +72,10 @@ The client side never touches Node sockets: `native.connect(clientGroup, url, ws
|
|
|
72
72
|
|
|
73
73
|
`Socket::sendTransformed` does not write when the socket is corkable (WebSockets only, plain TCP only) and `NodeData::corkState->enabled` is set. It frames the message into the socket's `messageQueue` and registers the socket in the per-loop `corkState->pending` list. `Socket::flushCorked` drains that list with one `sendmsg`/`WSASend` gather per socket from two libuv hooks registered in `registerCheck` (`src/Addon.h`): a `uv_prepare` (before the loop blocks in poll) and the existing `uv_check` (after poll and after Node's immediates). Anything that cannot be written falls through to the normal `UV_WRITABLE` drain loop in `ioHandler`. `closeSocket`/`onEnd`/`transfer` call `flushCorkedOnClose`, which unregisters the socket and pushes what it can to the kernel without invoking callbacks. `CWS_CORK=0` disables it at addon load (`corkEnabledFromEnv`). `HttpSocket` is never corkable because `Hub::upgrade` deletes it synchronously.
|
|
74
74
|
|
|
75
|
+
### Send worker thread
|
|
76
|
+
|
|
77
|
+
`SendWorker` (`src/SendWorker.cpp`) starts one `std::thread` at addon load when corking is enabled. `Socket::uncork` moves up to 512 queued frames into a `Socket::SendOp` (ownership moves with them) and hands it over through `deps/readerwriterqueue` (blocking SPSC main→worker, plain SPSC worker→main plus a `uv_async`). The worker only calls `sendmsg`/`WSASend` and fills `result`/`error`; `Socket::sendComplete` on the main thread pops what was sent, requeues the rest at the head, resubmits if more queued, arms `UV_WRITABLE` for the classic drain loop on a short write, runs send callbacks, and calls `endCb` (= `STATE::onEnd`, set in `setState`) on a hard error. While an op is in flight `write()` appends to the queue and the drain loop stays out. `closeSocket` orphans an in-flight op (`socket = nullptr`, `closeFd = true`) and lets the completion close the fd, so a reused fd number can never receive the old socket's bytes. SSL sockets never use the worker. `CWS_SEND_THREAD=0` disables. Compressed sends: `WebSocket::send` queues the raw payload as a `compressPending` message (`enqueueCompressPending`); `performSend` deflates + frames it on the worker with the socket's window (`op->deflateWindow`) or the worker's own shared compressor, then builds the iovecs. Any main-thread write path calls `materializePending()` first (`WebSocket::materialize` via `materializeCb`, using the hub's compressor). A socket closed mid-flight hands its window to the op (`destroyWindow`, `workerOwnsWindow`) so `onEnd` does not free it under the worker.
|
|
78
|
+
|
|
75
79
|
### Things that are easy to get wrong
|
|
76
80
|
|
|
77
81
|
- `dist/*.js` and `dist/bindings/*.node` are committed. Rebuild TS with `npm run build-ts` and commit the output; binaries must be rebuilt on each platform when C++ changes.
|
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ This table is true if you run ssl directly with `cws` (`Node.js`). In case if yo
|
|
|
14
14
|
|
|
15
15
|
| cWS Version | Node 26 | Node 24 | Node 22 | Node 20 |
|
|
16
16
|
|-------------|---------|----------|---------|----------
|
|
17
|
+
| 4.10.0 | X | X | X | X |
|
|
18
|
+
| 4.9.0 | X | X | X | X |
|
|
17
19
|
| 4.8.4 | X | X | X | X |
|
|
18
20
|
| 4.8.3 | X | X | X | X |
|
|
19
21
|
| 4.8.2 | X | X | X | X |
|
|
@@ -185,6 +187,8 @@ const wsServer = new WebSocket.Server({
|
|
|
185
187
|
* sliding window (streaming compression; far better on small messages). windowBits 9..15 and
|
|
186
188
|
* memLevel 1..9 pick its memory tier: 15/8 = ~256 KB per socket (default), 12/5 = ~32 KB, 10/3 = ~8 KB.
|
|
187
189
|
* threshold = minimum message size in bytes to compress (default 0); send(..., { compress }) overrides.
|
|
190
|
+
* Independent messages (the shared mode) are compressed by the built-in microdeflate encoder, ~1.7x faster
|
|
191
|
+
* than zlib-ng level 1 at the same ratio; CWS_MICRO_DEFLATE=0 falls back to zlib-ng.
|
|
188
192
|
* level 1..9 (default 2): deflate level of the per-socket compressor. The prebuilt bindings use zlib-ng
|
|
189
193
|
* (see `zlibBackend` export): level 1 is its very fast "quick" strategy (~3-4x less CPU than zlib
|
|
190
194
|
* level 1 for ~8% worse ratio), level 2 matches zlib level 1's ratio.
|
|
@@ -282,6 +286,14 @@ All `send()` calls made to a socket during one event-loop iteration are framed i
|
|
|
282
286
|
|
|
283
287
|
Set `CWS_CORK=0` in the environment to disable it and write every message immediately (previous behaviour). Corking applies to plain TCP sockets only; TLS sockets always write immediately.
|
|
284
288
|
|
|
289
|
+
### Send worker thread (performance)
|
|
290
|
+
|
|
291
|
+
The gathered write itself (the kernel's TCP work, which dominates a busy socket server's CPU) runs on a dedicated worker thread, not on the JavaScript thread. The end-of-tick flush hands each socket's frames to the worker through a lock-free queue; the worker performs the send and reports back, and completions, callbacks and `bufferedAmount` are handled on the main thread as before. Per-socket ordering is preserved, `send()` followed by `close()`/`terminate()` in the same tick still delivers, and a socket that closes while its send is in flight keeps its fd open until the send has finished. Measured on a busy 96-thread EPYC: 30-65% less main-thread CPU per message and 1.7-2.2x fan-out throughput, for 10-25% more total CPU on the worker.
|
|
292
|
+
|
|
293
|
+
With permessage-deflate enabled, compression of outgoing messages also runs on the worker: `send()` queues the raw payload and the worker deflates and frames it before writing, so a compressed `send()` costs the JavaScript thread the same as an uncompressed one (measured: 6 µs → 0.7 µs per 2 KB message).
|
|
294
|
+
|
|
295
|
+
Set `CWS_SEND_THREAD=0` to disable it (sends and compression then happen on the main thread at the end of the tick). The `sendThread` export reports `'active'` or the reason it is not. TLS sockets always send on the main thread.
|
|
296
|
+
|
|
285
297
|
### Secure WebSocket
|
|
286
298
|
You can use `wss://` with `cws` by providing `https` server to `cws` and setting `secureProtocol` on https options:
|
|
287
299
|
|
package/binding.gyp
CHANGED
|
@@ -13,11 +13,13 @@
|
|
|
13
13
|
'src/WebSocket.cpp',
|
|
14
14
|
'src/HTTPSocket.cpp',
|
|
15
15
|
'src/Socket.cpp',
|
|
16
|
-
'src/Zlib.cpp'
|
|
16
|
+
'src/Zlib.cpp',
|
|
17
|
+
'src/SendWorker.cpp'
|
|
17
18
|
],
|
|
18
19
|
# Node private headers vendored per major (see src/headers/); the Makefile passes the same -I.
|
|
19
20
|
'include_dirs': [
|
|
20
|
-
'src/headers/<!(node -p "parseInt(process.versions.node)")'
|
|
21
|
+
'src/headers/<!(node -p "parseInt(process.versions.node)")',
|
|
22
|
+
'deps/readerwriterqueue'
|
|
21
23
|
],
|
|
22
24
|
'defines': ['HAVE_OPENSSL=1', 'NODE_WANT_INTERNALS=1'],
|
|
23
25
|
'conditions': [
|
|
@@ -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.
|
|
@@ -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
|