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
10 changes: 10 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
* text=auto eol=lf

*.cpp text eol=lf
*.h text eol=lf
*.hpp text eol=lf
*.mk text eol=lf
Makefile text eol=lf
*.sh text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
3 changes: 2 additions & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
github: doctorlai
patreon: doctorlai
buy_me_a_coffee: y0BtG5R
thanks_dev: doctorlai
custom:
["https://www.buymeacoffee.com/y0BtG5R", "https://paypal.me/doctorlai/5"]
27 changes: 9 additions & 18 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,20 @@ assignees: ''
**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**To reproduce**
List the example, command, arguments, and sanitizer configuration that reproduce the behavior.

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment**
- OS and version:
- Compiler and version:
- Sanitizer (`address`, `thread`, `undefined`, or none):
- Commit SHA:

**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Output**
Include the complete compiler error, sanitizer report, or program output.

**Additional context**
Add any other context about the problem here.
21 changes: 21 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Summary

Describe the problem and the approach taken.

## Validation

- [ ] `make lint`
- [ ] Relevant example tests pass
- [ ] `make check` passes, or any unavailable check is explained below
- [ ] `make coverage` remains at or above the required threshold

## Checklist

- [ ] The change is focused and follows the existing C++ style
- [ ] New behavior has meaningful tests
- [ ] Documentation and `CHANGELOG.md` are updated when appropriate
- [ ] No generated binaries or coverage files are included

## Additional context

Include sanitizer output, performance notes, or follow-up work when relevant.
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
labels:
- dependencies
- github-actions
open-pull-requests-limit: 5
105 changes: 85 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,102 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

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

jobs:
build-and-run:
address-and-undefined-sanitizers:
runs-on: ubuntu-latest
env:
ASAN_OPTIONS: detect_leaks=1 # automatically detect memory leaks
ASAN_OPTIONS: detect_leaks=1
steps:
# 1. Checkout repo
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

# 2. Install build tools
- name: Install build tools
run: |
sudo apt-get update
sudo apt-get install -y build-essential g++-14 clang clang-format libtbb-dev
sudo apt-get install -y g++-14 make clang-format libtbb-dev

- name: Run all checks
run: make check SANITIZERS="address undefined"

thread-sanitizer:
runs-on: ubuntu-latest
env:
TBB_SOURCE_COMMIT: 8b829acc65569019edb896c5150d427f288e8aba
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

# 3. Clang-format check
- name: Clang-format Check
- name: Install build tools
run: |
./clang-check.sh *.cpp *.hpp *.c *.h
sudo apt-get update
sudo apt-get install -y g++-14 make cmake ninja-build git

# 4. Build and run all examples dynamically
- name: Build and Run
- name: Build ThreadSanitizer-aware oneTBB
run: |
for san in address thread undefined; do
echo "=== Building all examples with sanitizer $san ==="
make SANITIZE=$san
echo "=== Running all examples with sanitizer $san ==="
make run
done

- name: Clean up build artifacts
tbb_install_prefix="${RUNNER_TEMP}/oneTBB-tsan"
git init "${RUNNER_TEMP}/oneTBB-source"
git -C "${RUNNER_TEMP}/oneTBB-source" remote add origin https://github.com/uxlfoundation/oneTBB.git
git -C "${RUNNER_TEMP}/oneTBB-source" fetch --depth=1 origin "${TBB_SOURCE_COMMIT}"
git -C "${RUNNER_TEMP}/oneTBB-source" checkout --detach FETCH_HEAD
cmake \
-S "${RUNNER_TEMP}/oneTBB-source" \
-B "${RUNNER_TEMP}/oneTBB-build" \
-G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_C_COMPILER=gcc-14 \
-DCMAKE_CXX_COMPILER=g++-14 \
-DCMAKE_INSTALL_PREFIX="${tbb_install_prefix}" \
-DCMAKE_INSTALL_LIBDIR=lib \
-DTBB_ENABLE_IPO=OFF \
-DTBBMALLOC_BUILD=OFF \
-DTBB_SANITIZE=thread \
-DTBB_STRICT=OFF \
-DTBB_TEST=OFF
cmake --build "${RUNNER_TEMP}/oneTBB-build" --target install --parallel
echo "TBB_INSTALL_PREFIX=${tbb_install_prefix}" >> "${GITHUB_ENV}"
echo "CPLUS_INCLUDE_PATH=${tbb_install_prefix}/include" >> "${GITHUB_ENV}"
echo "LIBRARY_PATH=${tbb_install_prefix}/lib" >> "${GITHUB_ENV}"
echo "LD_LIBRARY_PATH=${tbb_install_prefix}/lib" >> "${GITHUB_ENV}"

- name: Build with ThreadSanitizer
run: make SANITIZE=thread build

- name: Verify instrumented oneTBB linkage
run: ldd parallel-transform/parallel-transform | grep -F "${TBB_INSTALL_PREFIX}/lib/libtbb.so"

- name: Run with ThreadSanitizer
run: make SANITIZE=thread test

coverage:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Install coverage tools
run: |
sudo apt-get update
sudo apt-get install -y g++-14 make gcovr jq libtbb-dev

- name: Generate coverage report
run: make coverage COVERAGE_MIN=0 COVERAGE_FUNCTION_MIN=0 COVERAGE_BRANCH_MIN=0

- name: Upload coverage report
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: coverage-report
path: coverage/
retention-days: 14

- name: Enforce coverage thresholds
run: |
make clean
jq --exit-status \
'.line_percent >= 80 and .function_percent >= 80 and .branch_percent >= 70' \
coverage/summary.json
119 changes: 119 additions & 0 deletions .github/workflows/coverage-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
name: Coverage Comment

on:
workflow_run:
workflows: [C++ CI]
types: [completed]

permissions:
actions: read
contents: read
pull-requests: write

concurrency:
group: coverage-comment-${{ github.event.workflow_run.head_repository.full_name }}-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: true

jobs:
comment:
if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Download coverage summary
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: coverage-report
path: coverage-data
repository: ${{ github.repository }}
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Create or update coverage comment
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
LINE_COVERAGE_THRESHOLD: '80'
FUNCTION_COVERAGE_THRESHOLD: '80'
BRANCH_COVERAGE_THRESHOLD: '70'
with:
script: |
const fs = require('fs');
const summary = JSON.parse(fs.readFileSync('coverage-data/summary.json', 'utf8'));

function readMetric(prefix, label, threshold) {
const percent = Number(summary[`${prefix}_percent`]);
const covered = Number(summary[`${prefix}_covered`]);
const total = Number(summary[`${prefix}_total`]);
const valid = Number.isFinite(percent)
&& Number.isInteger(covered)
&& Number.isInteger(total)
&& percent >= 0
&& percent <= 100
&& covered >= 0
&& total >= covered;

if (!valid) {
throw new Error(`Invalid ${label} coverage summary`);
}

return { label, percent, covered, total, threshold };
}

const metrics = [
readMetric('line', 'Lines', Number(process.env.LINE_COVERAGE_THRESHOLD)),
readMetric('function', 'Functions', Number(process.env.FUNCTION_COVERAGE_THRESHOLD)),
readMetric('branch', 'Branches', Number(process.env.BRANCH_COVERAGE_THRESHOLD)),
];
const rows = metrics.map((metric) => {
const status = metric.percent >= metric.threshold ? 'PASS' : 'FAIL';
return `| ${status} | ${metric.label} | ${metric.percent.toFixed(2)}% (${metric.threshold}% target) | ${metric.covered} / ${metric.total} |`;
});
const run = context.payload.workflow_run;
const associatedPulls = await github.paginate(
github.rest.repos.listPullRequestsAssociatedWithCommit,
{
...context.repo,
commit_sha: run.head_sha,
per_page: 100,
},
);
const pull = associatedPulls.find((candidate) =>
candidate.state === 'open' && candidate.head.sha === run.head_sha);
if (!pull) {
core.notice(`No open pull request currently points to ${run.head_sha}`);
return;
}

const marker = '<!-- cpp-coverage-report -->';
const body = [
marker,
'## Coverage Report',
'',
'| Status | Category | Percentage | Covered / Total |',
'|:--:|---|---:|---:|',
...rows,
'',
`[Generated in workflow #${run.run_number}](${run.html_url}) for commit \`${run.head_sha.slice(0, 7)}\` by the C++ coverage workflow.`,
].join('\n');

const issue_number = pull.number;
const comments = await github.paginate(github.rest.issues.listComments, {
...context.repo,
issue_number,
per_page: 100,
});
const previous = comments.find((comment) =>
comment.user.type === 'Bot' && comment.body.includes(marker));

if (previous) {
await github.rest.issues.updateComment({
...context.repo,
comment_id: previous.id,
body,
});
} else {
await github.rest.issues.createComment({
...context.repo,
issue_number,
body,
});
}
24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
# Object files
*.o
*.d
*.gcda
*.gcno

# Executable
thread_safe_queue
unique_ptr_basics
*.out
fold-left-fold-right/fold-left-fold-right
integer-factorization/integer-factorization
map-with-unknown-key/map-with-unknown-key
multithread-sum/multithread-sum
oop-with-exception/oop-with-exception
parallel-integral-pi/parallel-integral-pi
parallel-transform/parallel-transform
pi-monte-carlo/pi-monte-carlo
pi/pi
prefix-sum/prefix-sum
ranges-find-in-vector-string/ranges-find-in-vector-string
rot47/rot47
smart-ptr/smart-ptr
sudoku-solver/sudoku-solver
thread-safe-queue/thread-safe-queue
unique-ptr-basics/unique-ptr-basics
views-zip-enumerate/views-zip-enumerate
virtual-interface/virtual-interface

# Generated reports
coverage/

# Backup files
*~
Expand Down
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Changelog

All notable changes to this repository are documented in this file.

The project uses date-based versions because it does not publish a package or release artifact.

## [2026-07-16]

### Added

- Make targets for formatting, linting, testing, sanitizer checks, and coverage.
- Aggregate coverage gates of 80% for lines and functions and 70% for branches, with HTML, XML, JSON, and text reports.
- Secure pull request coverage comments and uploaded coverage artifacts.
- Immutable commit pins for third-party GitHub Actions used by CI.
- Contribution, support, security, privacy, pull request, and dependency update policies.

### Changed

- CI now performs clean builds for AddressSanitizer, ThreadSanitizer, and UndefinedBehaviorSanitizer.
- The CI ThreadSanitizer job now builds a pinned, sanitizer-aware oneTBB to avoid reports caused by an uninstrumented
parallel STL backend.
- UndefinedBehaviorSanitizer findings now terminate the affected test immediately.
- Local WSL2 checks skip the unsupported GCC ThreadSanitizer runtime by default while native Linux CI retains it.
- Example link rules now honor shared linker flags.
- Pi tests validate numeric output without requiring `bc`.
- Sudoku and smart pointer examples print real line breaks instead of escaped text.
- README build instructions, CI documentation, project commands, and badges were expanded and corrected.
Loading
Loading