|
3 | 3 | [](https://github.com/andreiavrammsd/cpp-channel/actions) [](https://codecov.io/github/andreiavrammsd/cpp-channel) |
4 | 4 | [](https://andreiavrammsd.github.io/cpp-channel/) |
5 | 5 |
|
6 | | -### Thread-safe container for sharing data between threads (synchronized queue). Header-only. Compatible with C++11 and newer. |
| 6 | +> Thread-safe container for sharing data between threads (synchronized queue). Header-only. Compatible with C++11 and newer. |
7 | 7 |
|
8 | 8 | ## About |
9 | 9 |
|
10 | 10 | `msd::channel` |
| 11 | + |
11 | 12 | * A synchronized queue that can be easily and safely shared between multiple threads. |
12 | 13 | * Tested with GCC, Clang, and MSVC. |
13 | 14 | * Uses [std::mutex](https://en.cppreference.com/w/cpp/thread/mutex.html) for synchronization. |
14 | 15 | * Uses a customizable `storage` to store elements. |
15 | 16 |
|
16 | 17 | It's a class that can be constructed in several ways: |
| 18 | + |
17 | 19 | * Buffered: |
18 | | - * The channel accepts a specified number of elements, after which it blocks the writer threads and waits for a reader thread to read an element. |
19 | | - * It blocks the reader threads when channel is empty until a writer thread writes elements. |
20 | | - * `msd::channel<int> chan{2};` |
| 20 | + * The channel accepts a specified number of elements, after which it blocks the writer threads and waits for a reader thread to read an element. |
| 21 | + * It blocks the reader threads when channel is empty until a writer thread writes elements. |
| 22 | + * `msd::channel<int> chan{2};` |
21 | 23 | * Unbuffered: |
22 | | - * Never blocks writes. |
23 | | - * It blocks the reader threads when channel is empty until a writer thread writes elements. |
24 | | - * `msd::channel<int> chan{};` |
| 24 | + * Never blocks writes. |
| 25 | + * It blocks the reader threads when channel is empty until a writer thread writes elements. |
| 26 | + * `msd::channel<int> chan{};` |
25 | 27 | * Heap- or stack-allocated: pass a custom storage or choose a [built-in storage](https://github.com/andreiavrammsd/cpp-channel/blob/master/include/msd/storage.hpp): |
26 | | - * `msd::queue_storage` (default): uses [std::queue](https://en.cppreference.com/w/cpp/container/queue.html) |
27 | | - * `msd::vector_storage`: uses [std::vector](https://en.cppreference.com/w/cpp/container/vector.html) (if cache locality is important) |
28 | | - * `msd::channel<int, msd::vector_storage<int>> chan{2};` |
29 | | - * `msd::array_storage` (always buffered): uses [std::array](https://en.cppreference.com/w/cpp/container/array.html) (if you want stack allocation) |
30 | | - * `msd::channel<int, msd::array_storage<int, 10>> chan{};` |
31 | | - * `msd::channel<int, msd::array_storage<int, 10>> chan{10}; // does not compile because capacity is already passed as template argument` |
32 | | - * aka `msd::static_channel<int, 10>` |
| 28 | + * `msd::queue_storage` (default): uses [std::queue](https://en.cppreference.com/w/cpp/container/queue.html) |
| 29 | + * `msd::vector_storage`: uses [std::vector](https://en.cppreference.com/w/cpp/container/vector.html) (if cache locality is important) |
| 30 | + * `msd::channel<int, msd::vector_storage<int>> chan{2};` |
| 31 | + * `msd::array_storage` (always buffered): uses [std::array](https://en.cppreference.com/w/cpp/container/array.html) (if you want stack allocation) |
| 32 | + * `msd::channel<int, msd::array_storage<int, 10>> chan{};` |
| 33 | + * `msd::channel<int, msd::array_storage<int, 10>> chan{10}; // does not compile because capacity is already passed as template argument` |
| 34 | + * aka `msd::static_channel<int, 10>` |
33 | 35 |
|
34 | 36 | A `storage` is: |
| 37 | + |
35 | 38 | * A class with a specific interface for storing elements. |
36 | 39 | * Must implement [FIFO](https://en.wikipedia.org/wiki/FIFO) logic. |
37 | 40 | * See [built-in storages](https://github.com/andreiavrammsd/cpp-channel/blob/master/include/msd/storage.hpp). |
38 | 41 |
|
39 | 42 | Exceptions: |
| 43 | + |
40 | 44 | * msd::operator<< throws `msd::closed_channel` if channel is closed. |
41 | 45 | * `msd::channel::write` returns `bool` status instead of throwing. |
42 | 46 | * Heap-allocated storages could throw. |
@@ -78,104 +82,85 @@ VERSION=X.Y.Z \ |
78 | 82 |
|
79 | 83 | ## Usage |
80 | 84 |
|
81 | | -```c++ |
82 | | -#include <cassert> |
| 85 | +```cpp |
| 86 | +// Unbuffered channel |
83 | 87 |
|
84 | 88 | #include <msd/channel.hpp> |
85 | 89 |
|
86 | | -int main() { |
87 | | - msd::channel<int> chan; // Unbuffered |
88 | | - |
89 | | - // Send to channel |
90 | | - chan << 1 << 2; |
91 | | - |
92 | | - // Read from channel |
93 | | - int first{}; |
94 | | - int second{}; |
| 90 | +int main() |
| 91 | +{ |
| 92 | + msd::channel<int> chan; |
95 | 93 |
|
96 | | - chan >> first >> second; |
| 94 | + chan << 1 << 2; // Send |
97 | 95 |
|
98 | | - assert(first == 1); |
99 | | - assert(second == 2); |
| 96 | + int first_value{}; |
| 97 | + int second_value{}; |
| 98 | + chan >> first_value >> second_value; // Receive |
| 99 | + chan.read(first_value); // Returns channel close status (true/false), blocks thread when channel is empty |
100 | 100 | } |
101 | 101 | ``` |
102 | 102 |
|
103 | | -```c++ |
104 | | -#include <cassert> |
105 | | - |
106 | | -#include <msd/channel.hpp> |
107 | | - |
108 | | -int main() { |
109 | | - msd::channel<int, msd::vector_storage<int>> chan{2}; // Buffered with vector storage |
| 103 | +```cpp |
| 104 | +// Buffered channel with custom storage |
110 | 105 |
|
111 | | - // Send to channel |
112 | | - chan << 1; // Throws if the channel is closed (after chan.close()) |
113 | | - assert(chan.write(2)); // Returns false if the channel is closed (after chan.close()) |
114 | | - chan << 3; // Blocks because the capacity is 2 (and no one reads from channel) |
115 | | -} |
116 | | -``` |
117 | | - |
118 | | -```c++ |
119 | 106 | #include <msd/channel.hpp> |
120 | 107 |
|
121 | | -int main() { |
122 | | - msd::channel<int> chan{2}; // Buffered |
123 | | - |
124 | | - int in = 1; |
125 | | - int out = 0; |
126 | | - |
127 | | - // Send to channel |
128 | | - chan << in; |
129 | | - chan << in; |
| 108 | +int main() |
| 109 | +{ |
| 110 | + msd::channel<int, msd::vector_storage<int>> chan{2}; |
130 | 111 |
|
131 | | - // Read from channel |
132 | | - chan.read(out); |
133 | | - chan >> out; |
134 | | - chan >> out; // Blocks because the channel is empty (and no one writes on it) |
| 112 | + chan << 1; // Throws if channel is closed |
| 113 | + chan.write(2); // Non-throwing write, returns channel close status (true/false) |
| 114 | + chan << 3; // Blocks thread (no space, no reader) |
135 | 115 | } |
136 | 116 | ``` |
137 | 117 |
|
138 | | -```c++ |
139 | | -#include <iostream> |
| 118 | +```cpp |
| 119 | +// Range-based iteration |
140 | 120 |
|
141 | 121 | #include <msd/channel.hpp> |
142 | 122 |
|
143 | | -int main() { |
144 | | - msd::channel<int, msd::vector_storage<int>> chan; |
| 123 | +#include <iostream> |
145 | 124 |
|
146 | | - int in1 = 1; |
147 | | - int in2 = 2; |
| 125 | +int main() |
| 126 | +{ |
| 127 | + msd::channel<int> chan{2}; |
148 | 128 |
|
149 | | - chan << in1 << in2; |
| 129 | + chan << 1 << 2; |
| 130 | + for (int value : chan) { |
| 131 | + if (chan.closed()) { |
| 132 | + // You can break before it's empty |
| 133 | + break; |
| 134 | + } |
150 | 135 |
|
151 | | - for (const auto out : chan) { // Blocks: waits forever for channel items |
152 | | - std::cout << out << '\n'; |
| 136 | + std::cout << value << '\n'; // Blocks thread until there is data to read or channel is closed and empty |
153 | 137 | } |
154 | 138 | } |
155 | 139 | ``` |
156 | 140 |
|
157 | | -```c++ |
| 141 | +```cpp |
| 142 | +// Channel with statically-allocated storage (always buffered) |
| 143 | + |
158 | 144 | #include <msd/static_channel.hpp> |
159 | 145 |
|
160 | | -int main() { |
161 | | - msd::static_channel<int, 2> chan{}; // Always buffered |
162 | | - // Same as msd::channel<int, msd::array_storage<int, 2>> |
| 146 | +#include <algorithm> |
| 147 | + |
| 148 | +int main() |
| 149 | +{ |
| 150 | + msd::static_channel<int, 2> src{}; |
| 151 | + msd::static_channel<int, 2> dst{}; |
163 | 152 |
|
164 | | - int in = 1; |
165 | | - int out = 0; |
| 153 | + src.write(1); |
| 154 | + src.write(2); |
| 155 | + src.close(); |
166 | 156 |
|
167 | | - // Send to channel |
168 | | - chan.write(in); |
169 | | - chan.write(in); |
| 157 | + std::copy_if(src.begin(), src.end(), msd::back_inserter(dst), [](int value) { return value % 2 == 0; }); |
170 | 158 |
|
171 | | - // Read from channel |
172 | | - chan.read(out); |
173 | | - chan.read(out); |
174 | | - chan.read(out); // Blocks because the channel is empty (and no one writes on it) |
| 159 | + dst.size(); // 1 |
175 | 160 | } |
176 | 161 | ``` |
177 | 162 |
|
178 | | -See [examples](https://github.com/andreiavrammsd/cpp-channel/tree/master/examples) and [documentation](https://andreiavrammsd.github.io/cpp-channel/). |
| 163 | +See [examples](https://github.com/andreiavrammsd/cpp-channel/tree/master/examples) and [tests](https://github.com/andreiavrammsd/cpp-channel/tree/master/tests). Read the [documentation](https://andreiavrammsd.github.io/cpp-channel/) for full API reference. |
179 | 164 |
|
180 | 165 | ## Known limitations |
181 | 166 |
|
|
0 commit comments