Skip to content
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
4 changes: 4 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BasedOnStyle: Google
ColumnLimit: 100
DerivePointerAlignment: false
IncludeBlocks: Preserve
34 changes: 34 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
# Google style plus correctness/concurrency checks. Naming follows the Google
# C++ Style Guide, with STL-style lower_case method names for container-like
# types (explicitly permitted by the guide for STL-consistent interfaces).
Checks: >
bugprone-*,
clang-analyzer-*,
concurrency-*,
google-*,
misc-*,
modernize-*,
performance-*,
readability-*,
-modernize-use-trailing-return-type,
-misc-include-cleaner
WarningsAsErrors: '*'
HeaderFilterRegex: 'include/cq/.*'
CheckOptions:
# Complexity contributed by macro expansion (GTest asserts, etc.) is not
# the author's complexity.
readability-function-cognitive-complexity.IgnoreMacros: 'true'
# `auto _` is the general placeholder idiom (a language feature in C++26).
readability-identifier-length.IgnoredVariableNames: '^_$'
readability-identifier-naming.ClassCase: CamelCase
readability-identifier-naming.StructCase: CamelCase
readability-identifier-naming.EnumCase: CamelCase
readability-identifier-naming.TypeAliasCase: CamelCase
readability-identifier-naming.FunctionCase: lower_case
readability-identifier-naming.VariableCase: lower_case
readability-identifier-naming.ParameterCase: lower_case
readability-identifier-naming.PrivateMemberSuffix: '_'
readability-identifier-naming.ConstexprVariablePrefix: 'k'
readability-identifier-naming.ConstexprVariableCase: CamelCase
readability-identifier-naming.NamespaceCase: lower_case
64 changes: 64 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: CI

on:
push:
branches: [main]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In-flight CI on main gets cancelled by the next merge. Push events on main all share group CI-refs/heads/main, so two quick merges cancel the first commit's run — its TSan result and the "last green main" bisect signal are lost.

Suggested change
cancel-in-progress: true
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}


env:
# Single parallelism knob: cmake --build and ctest read these from the
# environment, and the lint job reuses it for xargs -P.
CMAKE_BUILD_PARALLEL_LEVEL: 4
CTEST_PARALLEL_LEVEL: 4

jobs:
test-tsan:
name: Tests (ThreadSanitizer, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Configure
run: cmake -B build-tsan -DENABLE_TSAN=ON -DCMAKE_BUILD_TYPE=Debug
- name: Build
run: cmake --build build-tsan
- name: Test
# --no-tests=error arms itself once tests/ lands; until then the
# scaffolding is allowed to no-op.
run: ctest --test-dir build-tsan --output-on-failure --no-tests="$([ -d tests ] && echo error || echo ignore)"

bench-build:
name: Benchmarks (Release, smoke-run)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure
run: cmake -B build-rel -DCMAKE_BUILD_TYPE=Release -DCQ_BUILD_TESTS=OFF
- name: Build
run: cmake --build build-rel
- name: Smoke-run benchmarks
run: ctest --test-dir build-rel --output-on-failure --no-tests="$([ -d bench ] && echo error || echo ignore)"

lint:
name: clang-format & clang-tidy
runs-on: ubuntu-latest
env:
# Pinned so a runner-image rollover can't change the formatting
# contract under us; bump deliberately.
CLANG_VERSION: 18

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version pin isn't backed by an install step. CLANG_VERSION: 18 only selects among binaries the runner image happens to preinstall, and runs-on: ubuntu-latest is unpinned. When ubuntu-latest rolls to an image that drops clang-18 (past rollovers dropped older majors), every lint run hard-fails with clang-format-18: command not found — the exact rollover event this comment says the pin defends against. Back the pin with runs-on: ubuntu-24.04 or an explicit LLVM 18 install step.

steps:
- uses: actions/checkout@v4
- name: clang-format
run: git ls-files '*.hpp' '*.ipp' '*.cpp' | xargs -r "clang-format-$CLANG_VERSION" --dry-run --Werror

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git ls-files | xargs breaks on unusual filenames. Whitespace in a path word-splits into nonexistent files (spurious lint failure), git C-quotes non-ASCII paths so xargs aborts on the quotes, and a --prefixed filename is parsed as an option (no --). NUL-delimiting fixes all three; the clang-tidy pipeline at lines 61-62 has the same issue.

Suggested change
run: git ls-files '*.hpp' '*.ipp' '*.cpp' | xargs -r "clang-format-$CLANG_VERSION" --dry-run --Werror
run: git ls-files -z '*.hpp' '*.ipp' '*.cpp' | xargs -0 -r "clang-format-$CLANG_VERSION" --dry-run --Werror

- name: clang-tidy
run: |
cmake -B build-lint
git ls-files '*.cpp' |

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy never covers the header-only product directly. git ls-files '*.cpp' selects only translation units, so the include/cq/ headers that HeaderFilterRegex names are tidied only transitively, when some .cpp happens to include them. In this PR there are zero .cpp files, so this step is a green no-op; later, a header added before (or without) a test that includes it merges with no tidy coverage at all, despite WarningsAsErrors: '*'. Consider tidying the public headers directly (e.g. a lint TU that includes every header under include/cq/).

xargs -r -P "$CMAKE_BUILD_PARALLEL_LEVEL" -n1 "clang-tidy-$CLANG_VERSION" -p build-lint
Comment on lines +63 to +64

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hand-rolled pipeline re-implements run-clang-tidy-18, which ships in the same ubuntu package as clang-tidy-18 and handles per-file fan-out, parallelism, quoting, and exit aggregation itself. The xargs form also couples lint concurrency to the unrelated CMAKE_BUILD_PARALLEL_LEVEL build knob — if that env block is ever dropped in favor of cmake --build --parallel, xargs -P "" aborts with "invalid number" and breaks this job from a distance. A path regex keeps it off the FetchContent _deps TUs:

Suggested change
git ls-files '*.cpp' |
xargs -r -P "$CMAKE_BUILD_PARALLEL_LEVEL" -n1 "clang-tidy-$CLANG_VERSION" -p build-lint
"run-clang-tidy-$CLANG_VERSION" -p build-lint 'tests/|bench/'

4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build*/

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unanchored pattern ignores nested source directories. build*/ matches at any depth and any name starting with "build" (verified with git check-ignore: tests/build_matrix/, bench/builders/, even docs/building/ are all ignored). Files added under such a directory silently never appear in git status and are omitted from commits.

Suggested change
build*/
/build*/

.cache/
compile_commands.json
.DS_Store
11 changes: 11 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
// Template implementation files are C++ (included from headers).
"files.associations": {
"*.ipp": "cpp"
},
// Relayout on save via the repo's .clang-format (Prettier-style).
// Works with either the clangd extension or Microsoft's C/C++ extension.
"[cpp]": {
"editor.formatOnSave": true

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatOnSave needs editor.defaultFormatter to be reliable. With both suggested extensions installed (clangd and Microsoft C/C++ — a common combination), VS Code hits the "multiple formatters" conflict and format-on-save silently does nothing, so the checked-in setting fails at its one job and CI's --dry-run --Werror rejects the push. Pick one, e.g. "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd" inside the [cpp] block.

}
}
66 changes: 66 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
cmake_minimum_required(VERSION 3.24)

project(concurrent_queue
VERSION 0.1.0
DESCRIPTION "Concurrent queues in C++20, from locked baseline to lock-free"
LANGUAGES CXX)

option(ENABLE_TSAN "Build with ThreadSanitizer" OFF)
option(CQ_BUILD_TESTS "Build unit and stress tests" ON)
option(CQ_BUILD_BENCHMARKS "Build Google Benchmark targets" ON)

# Every build tree gets a compile_commands.json for clangd/clang-tidy.
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Header-only library; consumers inherit C++20 via the compile feature.
add_library(cq INTERFACE)
add_library(cq::cq ALIAS cq)
target_include_directories(cq INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_compile_features(cq INTERFACE cxx_std_20)

add_library(cq_warnings INTERFACE)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cq_warnings is free-floating, so STYLE.md's enforcement claim doesn't hold. Nothing links cq_warnings here, and nothing makes future targets link it — STYLE.md's "tag hygiene is compiler-enforced" via -Wdocumentation relies on each consumer voluntarily opting in, and even then only the macOS job compiles with clang (both ubuntu jobs default to GCC, which has no -Wdocumentation). A target linking only cq::cq builds stale @param tags green on every CI job. Consider attaching the warnings to cq itself behind $<BUILD_INTERFACE:...>, or at least noting the contract's actual scope in STYLE.md.

target_compile_options(cq_warnings INTERFACE
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-Wall -Wextra -Wpedantic -Wconversion>
# Validates Doxygen comments against signatures (clang-only).
$<$<CXX_COMPILER_ID:Clang,AppleClang>:-Wdocumentation>
$<$<CXX_COMPILER_ID:MSVC>:/W4>)

# Applied only to our own targets: instrumenting the FetchContent deps just
# slows the build, and TSan still intercepts their pthread-level sync.
add_library(cq_sanitizers INTERFACE)
if(ENABLE_TSAN)
target_compile_options(cq_sanitizers INTERFACE
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-fsanitize=thread -fno-omit-frame-pointer>)
target_link_options(cq_sanitizers INTERFACE
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-fsanitize=thread>)
endif()

include(FetchContent)
enable_testing()

# The tests/ and bench/ sources land in follow-up PRs; each block is a no-op
# until its directory exists so the build scaffolding can merge first.
if(CQ_BUILD_TESTS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt")
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.tar.gz
URL_HASH SHA256=7b42b4d6ed48810c5362c265a17faebe90dc2373c885e5216439d37927f02926
DOWNLOAD_EXTRACT_TIMESTAMP TRUE)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DOWNLOAD_EXTRACT_TIMESTAMP TRUE re-opts into the behavior CMP0135 fixed. With cmake_minimum_required(3.24) the policy is NEW, defaulting to extraction-time timestamps; TRUE restores archive timestamps, so bumping the URL to a new gtest/benchmark version can leave extracted files older than existing build stamps and skip rebuilds in incremental trees (the exact bug the policy was created for). Same at line 61. Cleanest is to drop the option from both blocks and let the policy default apply.

Suggested change
DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
DOWNLOAD_EXTRACT_TIMESTAMP FALSE)

set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
add_subdirectory(tests)
endif()

if(CQ_BUILD_BENCHMARKS AND NOT ENABLE_TSAN
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/bench/CMakeLists.txt")
FetchContent_Declare(
benchmark
URL https://github.com/google/benchmark/archive/refs/tags/v1.9.1.tar.gz
URL_HASH SHA256=32131c08ee31eeff2c8968d7e874f3cb648034377dfc32a4c377fa8796d84981
DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(benchmark)
add_subdirectory(bench)
endif()
36 changes: 36 additions & 0 deletions STYLE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Style guide

This project follows the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
for formatting and naming, enforced by `.clang-format` and `.clang-tidy` (both
checked in CI).

## Comments

- **Public API** (everything under `include/cq/`): Doxygen `///` comments on
every public class and member. Use `@tparam`, `@param` / `@param[out]`,
`@return`, and `@throws` tags. State the contract: blocking behavior,
error/close semantics, ownership, and thread-safety.
- **Internal code** (tests, benchmarks, private members, function bodies):
plain `//` prose. Explain *why*, not *what*.
- Tag hygiene is compiler-enforced: clang builds compile with
`-Wdocumentation`, which rejects `@param` names that do not match the
signature. Keep comments in sync with code or the build fails.
- `TODO(username): description` for known follow-ups.

## Layout

- Headers (`.hpp`) declare; template member definitions live in a matching
`.ipp` included at the bottom of the header. No function bodies in class
definitions.
- Special member functions (constructors, copy/move operations, destructor)
stay grouped at the top of the `public:` section, with a comment explaining
any deleted operations.

## Tooling

- Format: `clang-format -i` (settings live in `.clang-format`). CI rejects
unformatted code; format-on-save settings are checked in
(`.vscode/settings.json`). CI pins clang-format/clang-tidy **18** — use the
same major version locally or formatting may not match.
- Lint: `clang-tidy -p <build-dir>` (settings live in `.clang-tidy`; every
build dir exports `compile_commands.json`).
Loading