Skip to content

Commit 2e1b9e8

Browse files
Onezerozero (#55)
* No clang-tidy action on save * Set copyright * Lock before checking if closed * Remove unused header * Remove redundant inline * Exclude clang tidy rules * Split CI jobs * Format files * Move implementation from inl to hpp * Set coverage threshold
1 parent 0ae02b9 commit 2e1b9e8

File tree

7 files changed

+135
-159
lines changed

7 files changed

+135
-159
lines changed

.clang-tidy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Checks: >
99
-hicpp-named-parameter,
1010
-readability-named-parameter,
1111
-altera-unroll-loops,
12+
-llvmlibc-inline-function-decl,
13+
-cppcoreguidelines-avoid-const-or-ref-data-members
1214
1315
WarningsAsErrors: "*"
1416

.github/workflows/cmake.yml

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
1-
# https://gist.github.com/NickNaso/0d478f1481686d5bcc868cac06620a60
21
name: build
32

43
on:
54
push:
65
branches:
76
- master
7+
paths-ignore:
8+
- '.vscode/**'
9+
- 'LICENSE'
10+
- 'README.md'
811
pull_request:
12+
paths-ignore:
13+
- '.vscode/**'
14+
- 'LICENSE'
15+
- 'README.md'
916
workflow_dispatch:
10-
schedule:
11-
- cron: '0 18 1 * *'
17+
18+
env:
19+
CTEST_OUTPUT_ON_FAILURE: ON
20+
CTEST_PARALLEL_LEVEL: 2
1221

1322
jobs:
14-
build:
23+
test:
1524
name: ${{ matrix.config.name }}
1625
runs-on: ${{ matrix.config.os }}
1726

@@ -23,65 +32,78 @@ jobs:
2332
name: "Ubuntu Latest GCC (Release)",
2433
os: ubuntu-latest,
2534
build_type: "Release",
26-
coverage: false,
2735
}
2836
- {
2937
name: "Ubuntu 22.04 GCC (Debug)",
3038
os: ubuntu-22.04,
3139
build_type: "Debug",
32-
coverage: true,
3340
}
3441
- {
3542
name: "macOS Latest Clang (Release)",
3643
os: macos-latest,
3744
build_type: "Release",
38-
coverage: false,
3945
}
4046
- {
4147
name: "Windows Latest (Release)",
4248
os: windows-latest,
4349
build_type: "Release",
44-
coverage: false,
4550
}
4651

4752
steps:
4853
- uses: actions/checkout@v4
4954

5055
- name: Create Build Environment
51-
# Some projects don't allow in-source building, so create a separate build directory
52-
# We'll use this as our working directory for all subsequent commands
5356
run: cmake -E make_directory ${{github.workspace}}/build
5457

5558
- name: Configure CMake
56-
# Use a bash shell so we can use the same syntax for environment variable
57-
# access regardless of the host operating system
58-
shell: bash
5959
working-directory: ${{github.workspace}}/build
60-
# Note the current convention is to use the -S and -B options here to specify source
61-
# and build directories, but this is only available with CMake 3.13 and higher.
62-
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
63-
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} -Dcpp_channel_build_examples=ON -Dcpp_channel_build_tests=ON
60+
run: cmake ${{ github.workspace }} -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} -Dcpp_channel_build_examples=ON -Dcpp_channel_build_tests=ON
6461

6562
- name: Build
6663
working-directory: ${{github.workspace}}/build
67-
shell: bash
68-
# Execute the build. You can specify a specific target with "--target <NAME>"
6964
run: cmake --build . --config ${{ matrix.config.build_type }} --target tests
7065

7166
- name: Test
7267
working-directory: ${{github.workspace}}/build
73-
shell: bash
74-
# Execute tests defined by the CMake configuration.
75-
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
7668
run: ctest -C ${{ matrix.config.build_type }} --verbose -R channel_test*
7769

70+
- name: Run examples
71+
working-directory: ${{github.workspace}}/build
72+
run: cmake --build . --config ${{ matrix.config.build_type }} --target examples
73+
74+
coverage:
75+
name: Coverage
76+
runs-on: ubuntu-latest
77+
78+
steps:
79+
- uses: actions/checkout@v4
80+
81+
- name: Create Build Environment
82+
run: cmake -E make_directory ${{github.workspace}}/build
83+
84+
- name: Configure CMake
85+
working-directory: ${{github.workspace}}/build
86+
run: cmake ${{ github.workspace }} -DCMAKE_BUILD_TYPE=Debug -Dcpp_channel_build_tests=ON
87+
88+
- name: Build
89+
working-directory: ${{github.workspace}}/build
90+
run: cmake --build . --config Debug --target tests
91+
92+
- name: Test
93+
working-directory: ${{github.workspace}}/build
94+
run: ctest -C Debug --verbose -R channel_test11
95+
7896
- name: Upload coverage reports to Codecov
7997
uses: codecov/[email protected]
8098
with:
8199
token: ${{ secrets.CODECOV_TOKEN }}
82-
if: matrix.config.coverage == true
83100

84-
- name: Run examples
85-
working-directory: ${{github.workspace}}/build
86-
shell: bash
87-
run: cmake --build . --config ${{ matrix.config.build_type }} --target examples
101+
clang-format:
102+
name: Clang Format
103+
runs-on: ubuntu-latest
104+
105+
steps:
106+
- uses: actions/checkout@v4
107+
108+
- name: Run Clang Format
109+
run: clang-format --dry-run --Werror $(find include -type f)

.vscode/settings.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33
"-Dcpp_channel_build_examples=ON",
44
"-Dcpp_channel_build_tests=ON"
55
],
6-
"editor.codeActionsOnSave": {
7-
"source.fixAll": "explicit"
8-
},
6+
"clang-tidy.fixOnSave": false,
7+
"clang-tidy.lintOnSave": false,
98
"editor.formatOnSave": true,
109
"clang-format.executable": "clang-format",
1110
"clang-format.style": "file",
1211
"editor.defaultFormatter": "xaver.clang-format",
1312
"files.insertFinalNewline": true,
14-
"files.trimFinalNewlines": true,
13+
"files.trimFinalNewlines": true
1514
}

codecov.yml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
coverage:
2-
precision: 2
3-
round: down
4-
range: "100...100"
52
status:
6-
project: true
7-
patch: true
8-
changes: true
9-
3+
patch:
4+
default:
5+
target: 96%
6+
threshold: 0%
7+
if_ci_failed: error
8+
project:
9+
default:
10+
target: 97%
11+
threshold: 0%
12+
if_ci_failed: error
1013
ignore:
1114
- 'tests/*'

include/msd/blocking_iterator.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2023 Andrei Avram
1+
// Copyright (C) 2020-2025 Andrei Avram
22

33
#ifndef MSD_CHANNEL_BLOCKING_ITERATOR_HPP_
44
#define MSD_CHANNEL_BLOCKING_ITERATOR_HPP_

include/msd/channel.hpp

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2023 Andrei Avram
1+
// Copyright (C) 2020-2025 Andrei Avram
22

33
#ifndef MSD_CHANNEL_HPP_
44
#define MSD_CHANNEL_HPP_
@@ -10,7 +10,6 @@
1010
#include <queue>
1111
#include <stdexcept>
1212
#include <type_traits>
13-
#include <utility>
1413

1514
#include "blocking_iterator.hpp"
1615

@@ -54,7 +53,7 @@ class channel {
5453
*
5554
* @param capacity Number of elements the channel can store before blocking.
5655
*/
57-
explicit constexpr channel(size_type capacity);
56+
explicit constexpr channel(const size_type capacity) : cap_{capacity} {}
5857

5958
/**
6059
* Pushes an element into the channel.
@@ -75,28 +74,35 @@ class channel {
7574
/**
7675
* Returns the number of elements in the channel.
7776
*/
78-
NODISCARD inline size_type constexpr size() const noexcept;
77+
NODISCARD size_type constexpr size() const noexcept { return size_; }
7978

8079
/**
8180
* Returns true if there are no elements in channel.
8281
*/
83-
NODISCARD inline bool constexpr empty() const noexcept;
82+
NODISCARD bool constexpr empty() const noexcept { return size_ == 0; }
8483

8584
/**
8685
* Closes the channel.
8786
*/
88-
inline void close() noexcept;
87+
inline void close() noexcept
88+
{
89+
{
90+
std::unique_lock<std::mutex> lock{mtx_};
91+
is_closed_.store(true);
92+
}
93+
cnd_.notify_all();
94+
}
8995

9096
/**
9197
* Returns true if the channel is closed.
9298
*/
93-
NODISCARD inline bool closed() const noexcept;
99+
NODISCARD inline bool closed() const noexcept { return is_closed_.load(); }
94100

95101
/**
96102
* Iterator
97103
*/
98-
iterator begin() noexcept;
99-
iterator end() noexcept;
104+
iterator begin() noexcept { return blocking_iterator<channel<T>>{*this}; }
105+
iterator end() noexcept { return blocking_iterator<channel<T>>{*this}; }
100106

101107
/**
102108
* Channel cannot be copied or moved.
@@ -115,13 +121,64 @@ class channel {
115121
std::condition_variable cnd_;
116122
std::atomic<bool> is_closed_{false};
117123

118-
inline void waitBeforeRead(std::unique_lock<std::mutex>&);
119-
inline void waitBeforeWrite(std::unique_lock<std::mutex>&);
124+
inline void waitBeforeRead(std::unique_lock<std::mutex>& lock)
125+
{
126+
cnd_.wait(lock, [this]() { return !empty() || closed(); });
127+
};
128+
129+
inline void waitBeforeWrite(std::unique_lock<std::mutex>& lock)
130+
{
131+
if (cap_ > 0 && size_ == cap_) {
132+
cnd_.wait(lock, [this]() { return size_ < cap_; });
133+
}
134+
}
135+
120136
friend class blocking_iterator<channel>;
121137
};
122138

123-
} // namespace msd
139+
template <typename T>
140+
channel<typename std::decay<T>::type>& operator<<(channel<typename std::decay<T>::type>& ch, T&& in)
141+
{
142+
{
143+
std::unique_lock<std::mutex> lock{ch.mtx_};
144+
ch.waitBeforeWrite(lock);
145+
146+
if (ch.closed()) {
147+
throw closed_channel{"cannot write on closed channel"};
148+
}
149+
150+
ch.queue_.push(std::forward<T>(in));
151+
++ch.size_;
152+
}
153+
154+
ch.cnd_.notify_one();
155+
156+
return ch;
157+
}
124158

125-
#include "channel.inl"
159+
template <typename T>
160+
channel<T>& operator>>(channel<T>& ch, T& out)
161+
{
162+
{
163+
std::unique_lock<std::mutex> lock{ch.mtx_};
164+
ch.waitBeforeRead(lock);
165+
166+
if (ch.closed() && ch.empty()) {
167+
return ch;
168+
}
169+
170+
if (!ch.empty()) {
171+
out = std::move(ch.queue_.front());
172+
ch.queue_.pop();
173+
--ch.size_;
174+
}
175+
}
176+
177+
ch.cnd_.notify_one();
178+
179+
return ch;
180+
}
181+
182+
} // namespace msd
126183

127184
#endif // MSD_CHANNEL_HPP_

0 commit comments

Comments
 (0)