Skip to content

Static channel #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ jobs:

- name: Build
working-directory: ${{github.workspace}}/build
run: cmake --build . --config ${{ matrix.config.build_type }} --target tests -j
run: cmake --build . --config ${{ matrix.config.build_type }} --target channel_tests -j

- name: Test
working-directory: ${{github.workspace}}/build
run: ctest -C ${{ matrix.config.build_type }} --verbose -R channel_test --output-on-failure -j
run: ctest -C ${{ matrix.config.build_type }} --verbose -L channel_tests --output-on-failure -j

- name: Run examples
working-directory: ${{github.workspace}}/build
Expand All @@ -102,11 +102,11 @@ jobs:

- name: Build
working-directory: ${{github.workspace}}/build
run: cmake --build . --config Debug --target tests -j
run: cmake --build . --config Debug --target channel_tests -j

- name: Test
working-directory: ${{github.workspace}}/build
run: ctest -C Debug --verbose -R channel_test -j
run: ctest -C Debug --verbose -L channel_tests -j

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
Expand Down
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"-DCPP_CHANNEL_BUILD_TESTS=ON",
"-DCPP_CHANNEL_COVERAGE=ON",
"-DCPP_CHANNEL_SANITIZERS=ON",
"-DCPP_CHANNEL_SANITIZE_THREADS=OFF",
"-DCMAKE_CXX_STANDARD=11",
"-DCMAKE_INSTALL_PREFIX=${workspaceFolder}/install",
],
Expand Down
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.12)
project(cpp_channel)
set(PROJECT_VERSION 1.0.1)
set(PROJECT_VERSION 1.1.0)

set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard")
set(CMAKE_CXX_STANDARD_REQUIRED YES)
Expand All @@ -18,6 +18,7 @@ option(CPP_CHANNEL_BUILD_TESTS "Build all of cpp_channel's own tests." OFF)
option(CPP_CHANNEL_BUILD_EXAMPLES "Build cpp_channel's example programs." OFF)
option(CPP_CHANNEL_COVERAGE "Generate test coverage." OFF)
option(CPP_CHANNEL_SANITIZERS "Build with sanitizers." OFF)
option(CPP_CHANNEL_SANITIZE_THREADS "Build with thread sanitizer." OFF)

if (CPP_CHANNEL_BUILD_TESTS)
enable_testing()
Expand Down
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* Close to prevent pushing and stop waiting to fetch.
* Integrates well with STL algorithms in some cases. Eg: std::move(ch.begin(), ch.end(), ...).
* Tested with GCC, Clang, and MSVC.
* Includes stack-based, exception-free alternative (static channel).

## Requirements

Expand All @@ -26,7 +27,7 @@ Choose one of the methods:
* [CMake FetchContent](https://github.com/andreiavrammsd/cpp-channel/tree/master/examples/cmake-project)
* [CMake install](https://cmake.org/cmake/help/latest/command/install.html)
```shell
VERSION=1.0.1 \
VERSION=1.1.0 \
&& wget https://github.com/andreiavrammsd/cpp-channel/archive/refs/tags/v$VERSION.zip \
&& unzip v$VERSION.zip \
&& cd cpp-channel-$VERSION \
Expand Down Expand Up @@ -111,6 +112,26 @@ int main() {
}
```

```c++
#include <msd/static_channel.hpp>

int main() {
msd::static_channel<int, 2> chan{}; // always buffered

int in = 1;
int out = 0;

// Send to channel
chan.write(in);
chan.write(in);

// Read from channel
chan.read(out);
chan.read(out);
chan.read(out); // blocking because channel is empty (and no one writes on it)
}
```

See [examples](https://github.com/andreiavrammsd/cpp-channel/tree/master/examples) and [documentation](https://andreiavrammsd.github.io/cpp-channel/).

<br>
Expand Down
3 changes: 3 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ add_example(example_multithreading multithreading.cpp)

add_example(example_streaming streaming.cpp)
run_example(example_streaming)

add_example(example_multithreading_static_channel multithreading_static_channel.cpp)
run_example(example_multithreading_static_channel)
47 changes: 47 additions & 0 deletions examples/multithreading_static_channel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <future>
#include <iostream>
#include <sstream>
#include <thread>

#include "msd/static_channel.hpp"

int main()
{
msd::static_channel<int, 50> chan{}; // always buffered

// Send to channel
const auto writer = [&chan](int begin, int end) {
for (int i = begin; i <= end; ++i) {
chan.write(i);

std::stringstream msg;
msg << "Sent " << i << " from " << std::this_thread::get_id() << "\n";
std::cout << msg.str();

std::this_thread::sleep_for(std::chrono::milliseconds(10)); // simulate work
}
chan.close();
};

const auto reader = [&chan]() {
for (const auto out : chan) { // blocking until channel is drained (closed and empty)
std::stringstream msg;
msg << "Received " << out << " on " << std::this_thread::get_id() << "\n";
std::cout << msg.str();

std::this_thread::sleep_for(std::chrono::milliseconds(200)); // simulate work
}
};

const auto reader_1 = std::async(std::launch::async, reader);
const auto reader_2 = std::async(std::launch::async, reader);
const auto reader_3 = std::async(std::launch::async, reader);
const auto writer_1 = std::async(std::launch::async, writer, 1, 50);
const auto writer_2 = std::async(std::launch::async, writer, 51, 100);

reader_1.wait();
reader_2.wait();
reader_3.wait();
writer_1.wait();
writer_2.wait();
}
2 changes: 1 addition & 1 deletion include/msd/blocking_iterator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class blocking_iterator {
*/
reference operator*()
{
chan_ >> value_;
chan_.read(value_);

return value_;
}
Expand Down
21 changes: 6 additions & 15 deletions include/msd/channel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,10 @@
#include <type_traits>

#include "blocking_iterator.hpp"
#include "nodiscard.hpp"

namespace msd {

#if (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))
#define NODISCARD [[nodiscard]]
#else
#define NODISCARD
#endif

/**
* @brief Exception thrown if trying to write on closed channel.
*/
Expand Down Expand Up @@ -134,11 +129,9 @@ class channel {
return false;
}

if (!(size_ == 0)) {
out = std::move(queue_.front());
queue_.pop();
--size_;
}
out = std::move(queue_.front());
queue_.pop();
--size_;
}

cnd_.notify_one();
Expand Down Expand Up @@ -202,7 +195,7 @@ class channel {
NODISCARD bool drained() noexcept
{
std::unique_lock<std::mutex> lock{mtx_};
return is_closed_ && size_ == 0;
return size_ == 0 && is_closed_;
}

/**
Expand Down Expand Up @@ -238,7 +231,7 @@ class channel {

void waitBeforeRead(std::unique_lock<std::mutex>& lock)
{
cnd_.wait(lock, [this]() { return !(size_ == 0) || is_closed_; });
cnd_.wait(lock, [this]() { return size_ > 0 || is_closed_; });
};

void waitBeforeWrite(std::unique_lock<std::mutex>& lock)
Expand All @@ -247,8 +240,6 @@ class channel {
cnd_.wait(lock, [this]() { return size_ < cap_; });
}
}

friend class blocking_iterator<channel>;
};

template <typename T>
Expand Down
16 changes: 16 additions & 0 deletions include/msd/nodiscard.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (C) 2020-2025 Andrei Avram

#ifndef MSD_NODISCARD_HPP_
#define MSD_NODISCARD_HPP_

namespace msd {

#if (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))
#define NODISCARD [[nodiscard]]
#else
#define NODISCARD
#endif

} // namespace msd

#endif // MSD_NODISCARD_HPP_
Loading