diff --git a/.githooks/pre-commit b/.githooks/pre-commit deleted file mode 100755 index f4680bb..0000000 --- a/.githooks/pre-commit +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -set -ueo pipefail - -# Ensure ARM64 homebrew and /usr/local/bin are in PATH first. -# This finds /usr/local/bin/lintball and finds newer Homebrew-installed bash. -export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH" - -if [ -f "${LINTBALL_DIR:-.}/bin/lintball" ]; then - "${LINTBALL_DIR:-.}/bin/lintball" pre-commit -elif [ -n "$(command -v lintball)" ]; then - lintball pre-commit -else - { - echo - echo "Error: could not find a lintball executable, but lintball's pre-commit hook is enabled." - echo - echo "Solutions:" - echo - echo '- Install lintball globally:' - echo ' npm install -g lintball' - echo '- And/or, ensure that that lintball can be found in PATH:' - # shellcheck disable=SC2016 - echo ' ln -s "$(command -v lintball)" /usr/local/bin/' - echo - echo 'Workarounds:' - echo - echo '- Disable all git hooks:' - echo ' git config --local core.hooksPath ""' - echo "- Delete ${BASH_SOURCE[0]}" - echo - } >&2 - exit 1 -fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b9d70aa..cfb357e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,22 +26,22 @@ jobs: matrix: include: - runs-on: macos-latest - nim-version: 'ref:version-1-6' + nim-version: 'ref:version-2-0' sanitize-threads: 'yes' timeout-minutes: 20 - runs-on: ubuntu-latest - nim-version: 'latest:1.6' + nim-version: 'latest:2.0' sanitize-threads: 'yes' timeout-minutes: 5 - runs-on: ubuntu-latest - nim-version: 'latest:1.4' + nim-version: 'latest:1.6' sanitize-threads: 'no' timeout-minutes: 5 - runs-on: ubuntu-latest - nim-version: 'latest:1.2' + nim-version: 'latest:1.4' sanitize-threads: 'no' timeout-minutes: 5 @@ -87,7 +87,7 @@ jobs: matrix: include: - arch: aarch64 - nim-version: 'ref:version-1-6' + nim-version: 'ref:version-2-0' steps: # Optimization: re-use cached Nim->C compilation diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index e56c2ef..0000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: lint - -# yamllint disable rule:truthy -on: - pull_request: - branches: ['*'] - push: - branches: ['*'] - tags: ['*'] -# yamllint enable rule:truthy - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - uses: actions/setup-node@v2 - with: - node-version: '16' - - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - - uses: asdf-vm/actions/install@v1 - with: - tool_versions: | - nim latest:1.6 - - - name: Install shfmt - uses: mfinelli/setup-shfmt@v2 - - - name: Install lintball - run: | - asdf local nim latest:1.6 - npm install -g lintball@1.6.0 - lintball install-tools --yes - - - name: Check for linter issues - shell: bash - run: | - set -uexo pipefail - - default_branch=devel - if [ "$GITHUB_REF" = "refs/heads/$default_branch" ]; then - # A push to the default branch. - # Check files which were changed in the most recent commit. - commitish="HEAD~1" - elif [ -n "$GITHUB_BASE_REF" ]; then - # A pull request. - # Check files which have changed between the merge base and the - # current commit. - commitish="$(git merge-base -a refs/remotes/origin/$GITHUB_BASE_REF $GITHUB_SHA)" - else - # A push to a non-default, non-PR branch. - # Check files which have changed between default branch and the current - # commit. - commitish="$(git merge-base -a refs/remotes/origin/${default_branch} $GITHUB_SHA)" - fi - # Get the list of changed files - files="$(git diff --name-only "$commitish")" - # Check if any lintball configuration was changed. If so, check all files. - status=0 - case "$files" in - *lintballrc.json* | *.yamllint.yml* | *lint.yml*) lintball check || status=$? ;; - *) lintball check --since "$commitish" || status=$? ;; - esac - if [ "$status" -gt 0 ]; then - echo - echo "The above issues were found by lintball." - echo "To detect and auto-fix issues before pushing, install lintball's git hooks." - echo "See https://github.com/elijahr/lintball" - echo - exit $status - fi diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml index 0679414..e618693 100644 --- a/.github/workflows/update-docs.yml +++ b/.github/workflows/update-docs.yml @@ -21,12 +21,12 @@ jobs: uses: asdf-vm/actions/install@v1 with: tool_versions: | - nim latest:1.6 + nim latest:2.0 - name: Generate documentation run: | . "${HOME}/.asdf/asdf.sh" - asdf local nim latest:1.6 + asdf local nim latest:2.0 rm -rf htmldocs nim doc --out:htmldocs src/lockfreequeues.nim mv htmldocs/theindex.html htmldocs/index.html diff --git a/.lintballrc.json b/.lintballrc.json deleted file mode 100644 index 6182428..0000000 --- a/.lintballrc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "check_args": { - "yamllint": "-c ./.yamllint.yml" - }, - "write_args": { - "yamllint": "-c ./.yamllint.yml" - }, - "use": { - "autoflake": false, - "autopep8": false, - "black": false, - "docformatter": false, - "isort": false, - "nimfmt": true, - "nimpretty": true, - "prettier": true, - "prettier-eslint": true, - "pylint": false, - "rubocop": false, - "shellcheck": true, - "shfmt": true, - "yamllint": true - }, - "ignores": ["*/.git/*", "*/Gemfile.lock", "*/Pipfile.lock", "*/venv/*"] -} diff --git a/.yamllint.yml b/.yamllint.yml deleted file mode 100644 index 73d4b5c..0000000 --- a/.yamllint.yml +++ /dev/null @@ -1,26 +0,0 @@ -yaml-files: - - '*.yaml' - - '*.yml' - -rules: - braces: enable - brackets: enable - colons: enable - commas: enable - comments: disable - comments-indentation: enable - document-end: disable - document-start: disable - empty-lines: enable - empty-values: disable - hyphens: enable - indentation: enable - key-duplicates: enable - key-ordering: disable - line-length: disable - new-line-at-end-of-file: enable - new-lines: enable - octal-values: disable - quoted-strings: disable - trailing-spaces: enable - truthy: enable diff --git a/README.md b/README.md index ea40d17..55d0ef1 100644 --- a/README.md +++ b/README.md @@ -49,11 +49,3 @@ CI runs the test suite for both C and C++ targets on: - macOS `x86_64` The test suite is also run with [LLVM thread sanitization](https://clang.llvm.org/docs/ThreadSanitizer.html) to check for data races. - -### Linting - -This project uses [lintball](https://github.com/elijahr/lintball) to auto-format code. Please ensure your changeset passes linting. Enable the githooks with: - -```sh -git config --local core.hooksPath .githooks -``` diff --git a/lockfreequeues.nimble b/lockfreequeues.nimble index a28cc75..0dd85fb 100644 --- a/lockfreequeues.nimble +++ b/lockfreequeues.nimble @@ -1,7 +1,7 @@ import os # Package -version = "3.0.0" +version = "3.1.0" author = "Elijah Shaw-Rutschman" description = "Lock-free queue implementations for Nim." license = "MIT" diff --git a/src/lockfreequeues/mupsic.nim b/src/lockfreequeues/mupsic.nim index e9a6ffd..aaa6e02 100644 --- a/src/lockfreequeues/mupsic.nim +++ b/src/lockfreequeues/mupsic.nim @@ -136,7 +136,7 @@ proc push*[N, P: static int, T]( return false newTail = incOrReset(prevTail, 1, N) - # validateHeadAndTail(head, newTail, N) + assert validateHeadAndTail(head, newTail, N) self.queue.producerTails[self.idx].release(newTail) if self.queue.prevProducerIdx.compareExchangeWeak( @@ -210,7 +210,7 @@ proc push*[N, P: static int, T]( count = avail newTail = incOrReset(prevTail, count, N) - # validateHeadAndTail(head, newTail, N) + assert validateHeadAndTail(head, newTail, N) self.queue.producerTails[self.idx].release(newTail) if self.queue.prevProducerIdx.compareExchangeWeak( diff --git a/src/lockfreequeues/ops.nim b/src/lockfreequeues/ops.nim index d68ed19..4fd2b5c 100644 --- a/src/lockfreequeues/ops.nim +++ b/src/lockfreequeues/ops.nim @@ -10,43 +10,38 @@ proc validateHeadOrTail*( value: int, capacity: int, -): void +): bool {.inline.} = ## Assert that the given `value` is in the range `0..<2*capacity`. - assert(value in 0..<2*capacity) + return (value in 0..<2*capacity) -proc index*( - value: int, - capacity: int, -): int - {.inline.} = - ## Given a head or tail `value` in the range `0..<2*capacity`, determine its - ## actual index in storage. - let val = value - let capacity = capacity - validateHeadOrTail(val, capacity) - result = - if val >= capacity: - val - capacity - else: - val - - -proc incOrReset*( - original: int, - amount: int, +proc validateHeadAndTail*( + head: int, + tail: int, capacity: int, -): int +): bool {.inline.} = - ## Given an `original` head or tail value and an `amount` to increment, either - ## increment `original` by `amount`, or reset from zero if - ## `original + amount >= 2 * capacity`. - validateHeadOrTail(original, capacity) - assert(amount in 0..capacity) - result = original + amount - if unlikely(result >= 2 * capacity): - result -= 2 * capacity + ## Assert that the given `head` and `tail` values are valid for the given + ## `capacity`. + assert validateHeadOrTail(head, capacity) + assert validateHeadOrTail(tail, capacity) + var count = tail - head + if count < 0: + # Case when front in [Capacity, 2*Capacity) + # and tail in [0, Capacity) range + # for example for a queue of capacity 7 that rolled twice: + # + # | 14 | | | 10 | 11 | 12 | 13 | + # ^ ^ + # back front + # + # front is at index 10 (real 3) + # back is at index 15 (real 1) + # back - front + capacity = 1 - 3 + 7 = 5 + count += 2 * capacity + + result = count >= 0 and count <= capacity proc used*( @@ -57,14 +52,22 @@ proc used*( {.inline.} = ## Determine how many slots are taken in storage given `head`, `tail`, and ## `capacity` values. - validateHeadOrTail(head, capacity) - validateHeadOrTail(tail, capacity) + assert validateHeadAndTail(head, tail, capacity) + result = tail - head + if result < 0: + # Case when front in [Capacity, 2*Capacity) + # and tail in [0, Capacity) range + # for example for a queue of capacity 7 that rolled twice: + # + # | 14 | | | 10 | 11 | 12 | 13 | + # ^ ^ + # back front + # + # front is at index 10 (real 3) + # back is at index 15 (real 1) + # back - front + capacity = 1 - 3 + 7 = 5 + result += 2 * capacity - result = - if tail >= head: - tail - head - else: - capacity - (index(head, capacity) - index(tail, capacity)) proc available*( @@ -78,6 +81,36 @@ proc available*( result = capacity - used(head, tail, capacity) +proc index*( + value: int, + capacity: int, +): int + {.inline.} = + ## Given a head or tail `value` in the range `0..<2*capacity`, determine its + ## actual index in storage. + assert validateHeadOrTail(value, capacity) + result = + if value >= capacity: + value - capacity + else: + value + +proc incOrReset*( + original: int, + amount: int, + capacity: int, +): int + {.inline.} = + ## Given an `original` head or tail value and an `amount` to increment, either + ## increment `original` by `amount`, or reset from zero if + ## `original + amount >= 2 * capacity`. + assert validateHeadOrTail(original, capacity) + assert amount in 0..capacity + result = original + amount + if result >= 2 * capacity: + result -= 2 * capacity + + proc full*( head: int, tail: int, @@ -85,9 +118,7 @@ proc full*( ): bool {.inline.} = ## Determine if storage is full given `head`, `tail`, and `capacity` values. - validateHeadOrTail(head, capacity) - validateHeadOrTail(tail, capacity) - return abs(tail - head) >= capacity + return used(head, tail, capacity) >= capacity proc empty*( @@ -97,7 +128,6 @@ proc empty*( ): bool {.inline.} = ## Determine if storage is empty given `head` and `tail` values. - validateHeadOrTail(head, capacity) - validateHeadOrTail(tail, capacity) + assert validateHeadAndTail(head, tail, capacity) return head == tail diff --git a/tests/t_ops.nim b/tests/t_ops.nim index 96c6cef..85535bc 100644 --- a/tests/t_ops.nim +++ b/tests/t_ops.nim @@ -12,394 +12,568 @@ import lockfreequeues/ops when (NimMajor, NimMinor) < (1, 3): type AssertionDefect = AssertionError +suite "validateHeadAndTail(head, tail, 4)": + + test "valid": + check(validateHeadAndTail(0, 0, 4) == true) + check(validateHeadAndTail(0, 1, 4) == true) + check(validateHeadAndTail(0, 2, 4) == true) + check(validateHeadAndTail(0, 3, 4) == true) + check(validateHeadAndTail(0, 4, 4) == true) + check(validateHeadAndTail(1, 1, 4) == true) + check(validateHeadAndTail(1, 2, 4) == true) + check(validateHeadAndTail(1, 3, 4) == true) + check(validateHeadAndTail(1, 4, 4) == true) + check(validateHeadAndTail(1, 5, 4) == true) + check(validateHeadAndTail(2, 2, 4) == true) + check(validateHeadAndTail(2, 3, 4) == true) + check(validateHeadAndTail(2, 4, 4) == true) + check(validateHeadAndTail(2, 5, 4) == true) + check(validateHeadAndTail(2, 6, 4) == true) + check(validateHeadAndTail(3, 3, 4) == true) + check(validateHeadAndTail(3, 4, 4) == true) + check(validateHeadAndTail(3, 5, 4) == true) + check(validateHeadAndTail(3, 6, 4) == true) + check(validateHeadAndTail(3, 7, 4) == true) + check(validateHeadAndTail(4, 0, 4) == true) + check(validateHeadAndTail(4, 4, 4) == true) + check(validateHeadAndTail(4, 5, 4) == true) + check(validateHeadAndTail(4, 6, 4) == true) + check(validateHeadAndTail(4, 7, 4) == true) + check(validateHeadAndTail(5, 0, 4) == true) + check(validateHeadAndTail(5, 1, 4) == true) + check(validateHeadAndTail(5, 5, 4) == true) + check(validateHeadAndTail(5, 6, 4) == true) + check(validateHeadAndTail(5, 7, 4) == true) + check(validateHeadAndTail(6, 0, 4) == true) + check(validateHeadAndTail(6, 1, 4) == true) + check(validateHeadAndTail(6, 2, 4) == true) + check(validateHeadAndTail(6, 6, 4) == true) + check(validateHeadAndTail(6, 7, 4) == true) + check(validateHeadAndTail(7, 0, 4) == true) + check(validateHeadAndTail(7, 1, 4) == true) + check(validateHeadAndTail(7, 2, 4) == true) + check(validateHeadAndTail(7, 3, 4) == true) + check(validateHeadAndTail(7, 7, 4) == true) + + test "invalid": + check(validateHeadAndTail(0, 5, 4) == false) + check(validateHeadAndTail(0, 6, 4) == false) + check(validateHeadAndTail(0, 7, 4) == false) + check(validateHeadAndTail(1, 0, 4) == false) + check(validateHeadAndTail(1, 6, 4) == false) + check(validateHeadAndTail(1, 7, 4) == false) + check(validateHeadAndTail(2, 0, 4) == false) + check(validateHeadAndTail(2, 1, 4) == false) + check(validateHeadAndTail(2, 7, 4) == false) + check(validateHeadAndTail(3, 0, 4) == false) + check(validateHeadAndTail(3, 1, 4) == false) + check(validateHeadAndTail(3, 2, 4) == false) + check(validateHeadAndTail(4, 1, 4) == false) + check(validateHeadAndTail(4, 2, 4) == false) + check(validateHeadAndTail(4, 3, 4) == false) + check(validateHeadAndTail(5, 2, 4) == false) + check(validateHeadAndTail(5, 3, 4) == false) + check(validateHeadAndTail(5, 4, 4) == false) + check(validateHeadAndTail(6, 3, 4) == false) + check(validateHeadAndTail(6, 4, 4) == false) + check(validateHeadAndTail(6, 5, 4) == false) + check(validateHeadAndTail(7, 4, 4) == false) + check(validateHeadAndTail(7, 5, 4) == false) + check(validateHeadAndTail(7, 6, 4) == false) + +suite "validateHeadOrTail(headOrTail, 4)": + + test "valid": + check(validateHeadOrTail(0, 4) == true) + check(validateHeadOrTail(1, 4) == true) + check(validateHeadOrTail(2, 4) == true) + check(validateHeadOrTail(3, 4) == true) + check(validateHeadOrTail(4, 4) == true) + check(validateHeadOrTail(5, 4) == true) + check(validateHeadOrTail(6, 4) == true) + check(validateHeadOrTail(7, 4) == true) + + test "invalid": + check(validateHeadOrTail(-1, 4) == false) + check(validateHeadOrTail(8, 4) == false) + check(validateHeadOrTail(9, 4) == false) + check(validateHeadOrTail(-2, 4) == false) + +suite "index(value, 4)": -suite "index(value, capacity)": - test "value notin 0..<2*capacity raises AssertionDefect": + test "all": + check(index(0, 4) == 0) + check(index(1, 4) == 1) + check(index(2, 4) == 2) + check(index(3, 4) == 3) + check(index(4, 4) == 0) + check(index(5, 4) == 1) + check(index(6, 4) == 2) + check(index(7, 4) == 3) + +suite "incOrReset(original, amount, 4)": + test "valid": + check(incOrReset(0, 0, 4) == 0) + check(incOrReset(0, 1, 4) == 1) + check(incOrReset(0, 2, 4) == 2) + check(incOrReset(0, 3, 4) == 3) + check(incOrReset(0, 4, 4) == 4) + check(incOrReset(1, 0, 4) == 1) + check(incOrReset(1, 1, 4) == 2) + check(incOrReset(1, 2, 4) == 3) + check(incOrReset(1, 3, 4) == 4) + check(incOrReset(1, 4, 4) == 5) + check(incOrReset(2, 0, 4) == 2) + check(incOrReset(2, 1, 4) == 3) + check(incOrReset(2, 2, 4) == 4) + check(incOrReset(2, 3, 4) == 5) + check(incOrReset(2, 4, 4) == 6) + check(incOrReset(3, 0, 4) == 3) + check(incOrReset(3, 1, 4) == 4) + check(incOrReset(3, 2, 4) == 5) + check(incOrReset(3, 3, 4) == 6) + check(incOrReset(3, 4, 4) == 7) + check(incOrReset(4, 0, 4) == 4) + check(incOrReset(4, 1, 4) == 5) + check(incOrReset(4, 2, 4) == 6) + check(incOrReset(4, 3, 4) == 7) + check(incOrReset(4, 4, 4) == 0) + check(incOrReset(5, 0, 4) == 5) + check(incOrReset(5, 1, 4) == 6) + check(incOrReset(5, 2, 4) == 7) + check(incOrReset(5, 3, 4) == 0) + check(incOrReset(5, 4, 4) == 1) + check(incOrReset(6, 0, 4) == 6) + check(incOrReset(6, 1, 4) == 7) + check(incOrReset(6, 2, 4) == 0) + check(incOrReset(6, 3, 4) == 1) + check(incOrReset(6, 4, 4) == 2) + check(incOrReset(7, 0, 4) == 7) + check(incOrReset(7, 1, 4) == 0) + check(incOrReset(7, 2, 4) == 1) + check(incOrReset(7, 3, 4) == 2) + check(incOrReset(7, 4, 4) == 3) + + test "invalid": expect(AssertionDefect): - discard index(-1, 4) + discard incOrReset(0, 5, 4) expect(AssertionDefect): - discard index(8, 4) - - test "capacity <= 0 raises AssertionDefect": + discard incOrReset(0, 6, 4) expect(AssertionDefect): - discard(index(0, -1)) + discard incOrReset(0, 7, 4) expect(AssertionDefect): - discard(index(0, 0)) - - test "all": - for value in 0..<4: - check(index(value, 4) == value) - for value in 4..<8: - check(index(value, 4) == value - 4) - - -suite "incOrReset(original, amount, capacity)": - test "original notin 0..<2*capacity raises AssertionDefect": + discard incOrReset(1, 5, 4) expect(AssertionDefect): - discard incOrReset(-1, 0, 4) + discard incOrReset(1, 6, 4) expect(AssertionDefect): - discard incOrReset(8, 0, 4) - - test "amount notin 0..capacity raises AssertionDefect": + discard incOrReset(1, 7, 4) expect(AssertionDefect): - discard incOrReset(0, -1, 4) + discard incOrReset(2, 5, 4) expect(AssertionDefect): - discard incOrReset(0, 8, 4) - - test "capacity <= 0 raises AssertionDefect": + discard incOrReset(2, 6, 4) expect(AssertionDefect): - discard(incOrReset(0, 1, -1)) + discard incOrReset(2, 7, 4) expect(AssertionDefect): - discard(incOrReset(0, 1, 0)) - - test "all": - for original in 0..<8: - for amount in 0..4: - let expected = - if original + amount < 8: - original + amount - else: - (original + amount) - 8 - check(incOrReset(original, amount, 4) == expected) - - -suite "used(head, tail, capacity)": - test "head notin 0..<2*capacity raises AssertionDefect": + discard incOrReset(3, 5, 4) expect(AssertionDefect): - discard used(-1, 0, 4) + discard incOrReset(3, 6, 4) expect(AssertionDefect): - discard used(8, 0, 4) - - test "tail notin 0..<2*capacity raises AssertionDefect": + discard incOrReset(3, 7, 4) expect(AssertionDefect): - discard used(0, -1, 4) - + discard incOrReset(4, 5, 4) expect(AssertionDefect): - discard used(0, 8, 4) - - test "capacity <= 0 raises AssertionDefect": + discard incOrReset(4, 6, 4) + expect(AssertionDefect): + discard incOrReset(4, 7, 4) + expect(AssertionDefect): + discard incOrReset(5, 5, 4) + expect(AssertionDefect): + discard incOrReset(5, 6, 4) + expect(AssertionDefect): + discard incOrReset(5, 7, 4) + expect(AssertionDefect): + discard incOrReset(6, 5, 4) + expect(AssertionDefect): + discard incOrReset(6, 6, 4) + expect(AssertionDefect): + discard incOrReset(6, 7, 4) expect(AssertionDefect): - discard(used(0, 0, -1)) + discard incOrReset(7, 5, 4) expect(AssertionDefect): - discard(used(0, 0, 0)) + discard incOrReset(7, 6, 4) + expect(AssertionDefect): + discard incOrReset(7, 7, 4) - test "all": + +suite "used(head, tail, 4)": + test "valid": check(used(0, 0, 4) == 0) check(used(0, 1, 4) == 1) check(used(0, 2, 4) == 2) check(used(0, 3, 4) == 3) check(used(0, 4, 4) == 4) - check(used(0, 5, 4) == 5) - check(used(0, 6, 4) == 6) - check(used(0, 7, 4) == 7) - check(used(1, 0, 4) == 3) check(used(1, 1, 4) == 0) check(used(1, 2, 4) == 1) check(used(1, 3, 4) == 2) check(used(1, 4, 4) == 3) check(used(1, 5, 4) == 4) - check(used(1, 6, 4) == 5) - check(used(1, 7, 4) == 6) - check(used(2, 0, 4) == 2) - check(used(2, 1, 4) == 3) check(used(2, 2, 4) == 0) check(used(2, 3, 4) == 1) check(used(2, 4, 4) == 2) check(used(2, 5, 4) == 3) check(used(2, 6, 4) == 4) - check(used(2, 7, 4) == 5) - check(used(3, 0, 4) == 1) - check(used(3, 1, 4) == 2) - check(used(3, 2, 4) == 3) check(used(3, 3, 4) == 0) check(used(3, 4, 4) == 1) check(used(3, 5, 4) == 2) check(used(3, 6, 4) == 3) check(used(3, 7, 4) == 4) check(used(4, 0, 4) == 4) - check(used(4, 1, 4) == 5) - check(used(4, 2, 4) == 6) - check(used(4, 3, 4) == 7) check(used(4, 4, 4) == 0) check(used(4, 5, 4) == 1) check(used(4, 6, 4) == 2) check(used(4, 7, 4) == 3) check(used(5, 0, 4) == 3) check(used(5, 1, 4) == 4) - check(used(5, 2, 4) == 5) - check(used(5, 3, 4) == 6) - check(used(5, 4, 4) == 3) check(used(5, 5, 4) == 0) check(used(5, 6, 4) == 1) check(used(5, 7, 4) == 2) check(used(6, 0, 4) == 2) check(used(6, 1, 4) == 3) check(used(6, 2, 4) == 4) - check(used(6, 3, 4) == 5) - check(used(6, 4, 4) == 2) - check(used(6, 5, 4) == 3) check(used(6, 6, 4) == 0) check(used(6, 7, 4) == 1) check(used(7, 0, 4) == 1) check(used(7, 1, 4) == 2) check(used(7, 2, 4) == 3) check(used(7, 3, 4) == 4) - check(used(7, 4, 4) == 1) - check(used(7, 5, 4) == 2) - check(used(7, 6, 4) == 3) check(used(7, 7, 4) == 0) - -suite "available(head, tail, capacity)": - - test "head notin 0..<2*capacity raises AssertionDefect": + test "invalid": expect(AssertionDefect): - discard available(-1, 0, 4) + discard used(0, 5, 4) expect(AssertionDefect): - discard available(8, 0, 4) - - test "tail notin 0..<2*capacity raises AssertionDefect": + discard used(0, 6, 4) expect(AssertionDefect): - discard available(0, -1, 4) + discard used(0, 7, 4) expect(AssertionDefect): - discard available(0, 8, 4) - - test "capacity <= 0 raises AssertionDefect": + discard used(1, 0, 4) expect(AssertionDefect): - discard(available(0, 0, -1)) + discard used(1, 6, 4) expect(AssertionDefect): - discard(available(0, 0, 0)) + discard used(1, 7, 4) + expect(AssertionDefect): + discard used(2, 0, 4) + expect(AssertionDefect): + discard used(2, 1, 4) + expect(AssertionDefect): + discard used(2, 7, 4) + expect(AssertionDefect): + discard used(3, 0, 4) + expect(AssertionDefect): + discard used(3, 1, 4) + expect(AssertionDefect): + discard used(3, 2, 4) + expect(AssertionDefect): + discard used(4, 1, 4) + expect(AssertionDefect): + discard used(4, 2, 4) + expect(AssertionDefect): + discard used(4, 3, 4) + expect(AssertionDefect): + discard used(5, 2, 4) + expect(AssertionDefect): + discard used(5, 3, 4) + expect(AssertionDefect): + discard used(5, 4, 4) + expect(AssertionDefect): + discard used(6, 3, 4) + expect(AssertionDefect): + discard used(6, 4, 4) + expect(AssertionDefect): + discard used(6, 5, 4) + expect(AssertionDefect): + discard used(7, 4, 4) + expect(AssertionDefect): + discard used(7, 5, 4) + expect(AssertionDefect): + discard used(7, 6, 4) - test "all": +suite "available(head, tail, 4)": + test "valid": check(available(0, 0, 4) == 4) check(available(0, 1, 4) == 3) check(available(0, 2, 4) == 2) check(available(0, 3, 4) == 1) check(available(0, 4, 4) == 0) - check(available(0, 5, 4) == -1) - check(available(0, 6, 4) == -2) - check(available(0, 7, 4) == -3) - check(available(1, 0, 4) == 1) check(available(1, 1, 4) == 4) check(available(1, 2, 4) == 3) check(available(1, 3, 4) == 2) check(available(1, 4, 4) == 1) check(available(1, 5, 4) == 0) - check(available(1, 6, 4) == -1) - check(available(1, 7, 4) == -2) - check(available(2, 0, 4) == 2) - check(available(2, 1, 4) == 1) check(available(2, 2, 4) == 4) check(available(2, 3, 4) == 3) check(available(2, 4, 4) == 2) check(available(2, 5, 4) == 1) check(available(2, 6, 4) == 0) - check(available(2, 7, 4) == -1) - check(available(3, 0, 4) == 3) - check(available(3, 1, 4) == 2) - check(available(3, 2, 4) == 1) check(available(3, 3, 4) == 4) check(available(3, 4, 4) == 3) check(available(3, 5, 4) == 2) check(available(3, 6, 4) == 1) check(available(3, 7, 4) == 0) check(available(4, 0, 4) == 0) - check(available(4, 1, 4) == -1) - check(available(4, 2, 4) == -2) - check(available(4, 3, 4) == -3) check(available(4, 4, 4) == 4) check(available(4, 5, 4) == 3) check(available(4, 6, 4) == 2) check(available(4, 7, 4) == 1) check(available(5, 0, 4) == 1) check(available(5, 1, 4) == 0) - check(available(5, 2, 4) == -1) - check(available(5, 3, 4) == -2) - check(available(5, 4, 4) == 1) check(available(5, 5, 4) == 4) check(available(5, 6, 4) == 3) check(available(5, 7, 4) == 2) check(available(6, 0, 4) == 2) check(available(6, 1, 4) == 1) check(available(6, 2, 4) == 0) - check(available(6, 3, 4) == -1) - check(available(6, 4, 4) == 2) - check(available(6, 5, 4) == 1) check(available(6, 6, 4) == 4) check(available(6, 7, 4) == 3) check(available(7, 0, 4) == 3) check(available(7, 1, 4) == 2) check(available(7, 2, 4) == 1) check(available(7, 3, 4) == 0) - check(available(7, 4, 4) == 3) - check(available(7, 5, 4) == 2) - check(available(7, 6, 4) == 1) check(available(7, 7, 4) == 4) - -suite "full(head, tail, capacity)": - - test "head notin 0..<2*capacity raises AssertionDefect": + test "invalid": expect(AssertionDefect): - discard full(-1, 0, 4) + discard available(0, 5, 4) expect(AssertionDefect): - discard full(8, 0, 4) - - test "tail notin 0..<2*capacity raises AssertionDefect": + discard available(0, 6, 4) expect(AssertionDefect): - discard full(0, -1, 4) + discard available(0, 7, 4) expect(AssertionDefect): - discard full(0, 8, 4) - - test "capacity <= 0 raises AssertionDefect": + discard available(1, 0, 4) + expect(AssertionDefect): + discard available(1, 6, 4) + expect(AssertionDefect): + discard available(1, 7, 4) + expect(AssertionDefect): + discard available(2, 0, 4) + expect(AssertionDefect): + discard available(2, 1, 4) + expect(AssertionDefect): + discard available(2, 7, 4) + expect(AssertionDefect): + discard available(3, 0, 4) + expect(AssertionDefect): + discard available(3, 1, 4) + expect(AssertionDefect): + discard available(3, 2, 4) expect(AssertionDefect): - discard(full(0, 0, -1)) + discard available(4, 1, 4) expect(AssertionDefect): - discard(full(0, 0, 0)) + discard available(4, 2, 4) + expect(AssertionDefect): + discard available(4, 3, 4) + expect(AssertionDefect): + discard available(5, 2, 4) + expect(AssertionDefect): + discard available(5, 3, 4) + expect(AssertionDefect): + discard available(5, 4, 4) + expect(AssertionDefect): + discard available(6, 3, 4) + expect(AssertionDefect): + discard available(6, 4, 4) + expect(AssertionDefect): + discard available(6, 5, 4) + expect(AssertionDefect): + discard available(7, 4, 4) + expect(AssertionDefect): + discard available(7, 5, 4) + expect(AssertionDefect): + discard available(7, 6, 4) - test "all": +suite "full(head, tail, 4)": + test "valid": check(full(0, 0, 4) == false) check(full(0, 1, 4) == false) check(full(0, 2, 4) == false) check(full(0, 3, 4) == false) check(full(0, 4, 4) == true) - check(full(0, 5, 4) == true) - check(full(0, 6, 4) == true) - check(full(0, 7, 4) == true) - check(full(1, 0, 4) == false) check(full(1, 1, 4) == false) check(full(1, 2, 4) == false) check(full(1, 3, 4) == false) check(full(1, 4, 4) == false) check(full(1, 5, 4) == true) - check(full(1, 6, 4) == true) - check(full(1, 7, 4) == true) - check(full(2, 0, 4) == false) - check(full(2, 1, 4) == false) check(full(2, 2, 4) == false) check(full(2, 3, 4) == false) check(full(2, 4, 4) == false) check(full(2, 5, 4) == false) check(full(2, 6, 4) == true) - check(full(2, 7, 4) == true) - check(full(3, 0, 4) == false) - check(full(3, 1, 4) == false) - check(full(3, 2, 4) == false) check(full(3, 3, 4) == false) check(full(3, 4, 4) == false) check(full(3, 5, 4) == false) check(full(3, 6, 4) == false) check(full(3, 7, 4) == true) check(full(4, 0, 4) == true) - check(full(4, 1, 4) == false) - check(full(4, 2, 4) == false) - check(full(4, 3, 4) == false) check(full(4, 4, 4) == false) check(full(4, 5, 4) == false) check(full(4, 6, 4) == false) check(full(4, 7, 4) == false) - check(full(5, 0, 4) == true) + check(full(5, 0, 4) == false) check(full(5, 1, 4) == true) - check(full(5, 2, 4) == false) - check(full(5, 3, 4) == false) - check(full(5, 4, 4) == false) check(full(5, 5, 4) == false) check(full(5, 6, 4) == false) check(full(5, 7, 4) == false) - check(full(6, 0, 4) == true) - check(full(6, 1, 4) == true) + check(full(6, 0, 4) == false) + check(full(6, 1, 4) == false) check(full(6, 2, 4) == true) - check(full(6, 3, 4) == false) - check(full(6, 4, 4) == false) - check(full(6, 5, 4) == false) check(full(6, 6, 4) == false) check(full(6, 7, 4) == false) - check(full(7, 0, 4) == true) - check(full(7, 1, 4) == true) - check(full(7, 2, 4) == true) + check(full(7, 0, 4) == false) + check(full(7, 1, 4) == false) + check(full(7, 2, 4) == false) check(full(7, 3, 4) == true) - check(full(7, 4, 4) == false) - check(full(7, 5, 4) == false) - check(full(7, 6, 4) == false) check(full(7, 7, 4) == false) - -suite "empty(head, tail, 4)": - - test "head notin 0..<2*capacity raises AssertionDefect": + test "invalid": expect(AssertionDefect): - discard empty(-1, 0, 4) + discard full(0, 5, 4) expect(AssertionDefect): - discard empty(8, 0, 4) - - test "tail notin 0..<2*capacity raises AssertionDefect": + discard full(0, 6, 4) + expect(AssertionDefect): + discard full(0, 7, 4) + expect(AssertionDefect): + discard full(1, 0, 4) + expect(AssertionDefect): + discard full(1, 6, 4) + expect(AssertionDefect): + discard full(1, 7, 4) + expect(AssertionDefect): + discard full(2, 0, 4) + expect(AssertionDefect): + discard full(2, 1, 4) + expect(AssertionDefect): + discard full(2, 7, 4) + expect(AssertionDefect): + discard full(3, 0, 4) + expect(AssertionDefect): + discard full(3, 1, 4) + expect(AssertionDefect): + discard full(3, 2, 4) + expect(AssertionDefect): + discard full(4, 1, 4) + expect(AssertionDefect): + discard full(4, 2, 4) + expect(AssertionDefect): + discard full(4, 3, 4) + expect(AssertionDefect): + discard full(5, 2, 4) + expect(AssertionDefect): + discard full(5, 3, 4) + expect(AssertionDefect): + discard full(5, 4, 4) expect(AssertionDefect): - discard empty(0, -1, 4) + discard full(6, 3, 4) expect(AssertionDefect): - discard empty(0, 8, 4) + discard full(6, 4, 4) + expect(AssertionDefect): + discard full(6, 5, 4) + expect(AssertionDefect): + discard full(7, 4, 4) + expect(AssertionDefect): + discard full(7, 5, 4) + expect(AssertionDefect): + discard full(7, 6, 4) - test "all": +suite "empty(head, tail, 4)": + test "valid": check(empty(0, 0, 4) == true) check(empty(0, 1, 4) == false) check(empty(0, 2, 4) == false) check(empty(0, 3, 4) == false) check(empty(0, 4, 4) == false) - check(empty(0, 5, 4) == false) - check(empty(0, 6, 4) == false) - check(empty(0, 7, 4) == false) - check(empty(1, 0, 4) == false) check(empty(1, 1, 4) == true) check(empty(1, 2, 4) == false) check(empty(1, 3, 4) == false) check(empty(1, 4, 4) == false) check(empty(1, 5, 4) == false) - check(empty(1, 6, 4) == false) - check(empty(1, 7, 4) == false) - check(empty(2, 0, 4) == false) - check(empty(2, 1, 4) == false) check(empty(2, 2, 4) == true) check(empty(2, 3, 4) == false) check(empty(2, 4, 4) == false) check(empty(2, 5, 4) == false) check(empty(2, 6, 4) == false) - check(empty(2, 7, 4) == false) - check(empty(3, 0, 4) == false) - check(empty(3, 1, 4) == false) - check(empty(3, 2, 4) == false) check(empty(3, 3, 4) == true) check(empty(3, 4, 4) == false) check(empty(3, 5, 4) == false) check(empty(3, 6, 4) == false) check(empty(3, 7, 4) == false) check(empty(4, 0, 4) == false) - check(empty(4, 1, 4) == false) - check(empty(4, 2, 4) == false) - check(empty(4, 3, 4) == false) check(empty(4, 4, 4) == true) check(empty(4, 5, 4) == false) check(empty(4, 6, 4) == false) check(empty(4, 7, 4) == false) check(empty(5, 0, 4) == false) check(empty(5, 1, 4) == false) - check(empty(5, 2, 4) == false) - check(empty(5, 3, 4) == false) - check(empty(5, 4, 4) == false) check(empty(5, 5, 4) == true) check(empty(5, 6, 4) == false) check(empty(5, 7, 4) == false) check(empty(6, 0, 4) == false) check(empty(6, 1, 4) == false) check(empty(6, 2, 4) == false) - check(empty(6, 3, 4) == false) - check(empty(6, 4, 4) == false) - check(empty(6, 5, 4) == false) check(empty(6, 6, 4) == true) check(empty(6, 7, 4) == false) check(empty(7, 0, 4) == false) check(empty(7, 1, 4) == false) check(empty(7, 2, 4) == false) check(empty(7, 3, 4) == false) - check(empty(7, 4, 4) == false) - check(empty(7, 5, 4) == false) - check(empty(7, 6, 4) == false) check(empty(7, 7, 4) == true) + + test "invalid": + expect(AssertionDefect): + discard empty(0, 5, 4) + expect(AssertionDefect): + discard empty(0, 6, 4) + expect(AssertionDefect): + discard empty(0, 7, 4) + expect(AssertionDefect): + discard empty(1, 0, 4) + expect(AssertionDefect): + discard empty(1, 6, 4) + expect(AssertionDefect): + discard empty(1, 7, 4) + expect(AssertionDefect): + discard empty(2, 0, 4) + expect(AssertionDefect): + discard empty(2, 1, 4) + expect(AssertionDefect): + discard empty(2, 7, 4) + expect(AssertionDefect): + discard empty(3, 0, 4) + expect(AssertionDefect): + discard empty(3, 1, 4) + expect(AssertionDefect): + discard empty(3, 2, 4) + expect(AssertionDefect): + discard empty(4, 1, 4) + expect(AssertionDefect): + discard empty(4, 2, 4) + expect(AssertionDefect): + discard empty(4, 3, 4) + expect(AssertionDefect): + discard empty(5, 2, 4) + expect(AssertionDefect): + discard empty(5, 3, 4) + expect(AssertionDefect): + discard empty(5, 4, 4) + expect(AssertionDefect): + discard empty(6, 3, 4) + expect(AssertionDefect): + discard empty(6, 4, 4) + expect(AssertionDefect): + discard empty(6, 5, 4) + expect(AssertionDefect): + discard empty(7, 4, 4) + expect(AssertionDefect): + discard empty(7, 5, 4) + expect(AssertionDefect): + discard empty(7, 6, 4) diff --git a/tests/t_wraparound.nim b/tests/t_wraparound.nim new file mode 100644 index 0000000..28f1a63 --- /dev/null +++ b/tests/t_wraparound.nim @@ -0,0 +1,63 @@ +import atomics +import options +import unittest + +import lockfreequeues + +suite "wraparound": + + test "basic": + var q = initSipsic[2, string]() + check q.head.load == 0 + check q.tail.load == 0 + check not full(q.head.load, q.tail.load, q.capacity) + check empty(q.head.load, q.tail.load, q.capacity) + + discard q.push "a" + check q.head.load == 0 + check q.tail.load == 1 + check not full(q.head.load, q.tail.load, q.capacity) + check not empty(q.head.load, q.tail.load, q.capacity) + + discard q.push "b" + check q.head.load == 0 + check q.tail.load == 2 + check full(q.head.load, q.tail.load, q.capacity) + check not empty(q.head.load, q.tail.load, q.capacity) + + check q.pop.get == "a" + check q.head.load == 1 + check q.tail.load == 2 + check not full(q.head.load, q.tail.load, q.capacity) + check not empty(q.head.load, q.tail.load, q.capacity) + + check q.pop.get == "b" + check q.head.load == 2 + check q.tail.load == 2 + check not full(q.head.load, q.tail.load, q.capacity) + check empty(q.head.load, q.tail.load, q.capacity) + + discard q.push "c" + check q.head.load == 2 + check q.tail.load == 3 + check not full(q.head.load, q.tail.load, q.capacity) + check not empty(q.head.load, q.tail.load, q.capacity) + + discard q.push "d" + check q.head.load == 2 + check q.tail.load == 0 + check full(q.head.load, q.tail.load, q.capacity) + check not empty(q.head.load, q.tail.load, q.capacity) + + check q.pop.get == "c" + check q.head.load == 3 + check q.tail.load == 0 + check not full(q.head.load, q.tail.load, q.capacity) + check not empty(q.head.load, q.tail.load, q.capacity) + + check q.pop.get == "d" + check q.head.load == 0 + check q.tail.load == 0 + + check not full(q.head.load, q.tail.load, q.capacity) + check empty(q.head.load, q.tail.load, q.capacity) diff --git a/tests/test.nim b/tests/test.nim index 67bfa2a..ec9f1bf 100644 --- a/tests/test.nim +++ b/tests/test.nim @@ -12,6 +12,7 @@ import ./t_mupsic_threaded import ./t_ops import ./t_sipsic import ./t_sipsic_threaded +import ./t_wraparound export t_atomic_dsl, @@ -21,4 +22,5 @@ export t_mupsic_threaded, t_ops, t_sipsic, - t_sipsic_threaded + t_sipsic_threaded, + t_wraparound