mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-22 19:47:17 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4034c866a3 | ||
|
|
4935cbaeb3 | ||
|
|
302a486c5f | ||
|
|
754e0b90b7 | ||
|
|
0ec6648c5f | ||
|
|
97fbea23cc | ||
|
|
e6055ddbe1 | ||
|
|
a5c76fde2d | ||
|
|
eed6527946 | ||
|
|
ddbc5f1a24 | ||
|
|
97a1824537 | ||
|
|
b44c87613c | ||
|
|
b3bbf50d40 | ||
|
|
53246e5b15 | ||
|
|
7e82b0660f | ||
|
|
8ef1bdd346 | ||
|
|
3a2d19f980 | ||
|
|
04eca6d6c3 | ||
|
|
768aef30dc | ||
|
|
97cddd6721 | ||
|
|
e4dbd7de3a | ||
|
|
646d2ce620 | ||
|
|
6641896e9f | ||
|
|
4a4fded2eb | ||
|
|
c8e767afa4 | ||
|
|
00eeb0a005 | ||
|
|
a18839d92d | ||
|
|
8c594c7ed9 | ||
|
|
ebd810b184 | ||
|
|
3e4e56d6bb | ||
|
|
76da5d4475 | ||
|
|
c0d0fd0d97 | ||
|
|
9aebb5ebea | ||
|
|
796f2f8f1e | ||
|
|
b190f2b14f | ||
|
|
6099940c2c | ||
|
|
0db7b766e6 | ||
|
|
ea6226b8b9 | ||
|
|
eae0a35415 | ||
|
|
bec82ee704 | ||
|
|
c6224c48ed | ||
|
|
5fb1429e3a | ||
|
|
b6526522fe | ||
|
|
61041155d2 | ||
|
|
5ea72b45f1 | ||
|
|
7385004952 | ||
|
|
a6fea1227f | ||
|
|
968f9e1bf3 | ||
|
|
70813b336a | ||
|
|
ab8350b558 | ||
|
|
072e11ecd1 | ||
|
|
f669b70451 | ||
|
|
62839b1531 | ||
|
|
9bddc54722 | ||
|
|
25b9b4d27d | ||
|
|
22ed85c3ad | ||
|
|
8c7a27d721 | ||
|
|
9266c23ef6 | ||
|
|
5e9ce16de6 | ||
|
|
8d4ea00453 | ||
|
|
bdcf094171 | ||
|
|
a42e8174d9 | ||
|
|
91004209e9 | ||
|
|
4c18dd867c | ||
|
|
fb6ece56f2 | ||
|
|
c85cd77b2f | ||
|
|
cb5259d7c5 | ||
|
|
42cced50fb | ||
|
|
0e815aa1ac | ||
|
|
21149a81e3 | ||
|
|
b78202a99a |
No files matched your search
+8
-1
@@ -90,6 +90,13 @@ CheckOptions:
|
||||
readability-identifier-naming.ClassCase: CamelCase
|
||||
readability-identifier-naming.StructCase: CamelCase
|
||||
readability-identifier-naming.UnionCase: CamelCase
|
||||
readability-identifier-naming.TypeAliasCase: CamelCase
|
||||
readability-identifier-naming.TypedefCase: CamelCase
|
||||
# Member type names mandated by std/Boost concepts. Subtrees that need more
|
||||
# than these carry their own .clang-tidy (include/xrpl/{beast,basics,protocol,
|
||||
# peerfinder}, src/test/beast); one-off cases use an inline NOLINT.
|
||||
readability-identifier-naming.TypeAliasIgnoredRegexp: "^(const_iterator|const_reference|difference_type|duration|iterator|iterator_category|key_type|mapped_type|period|pointer|reference|rep|result_type|size_type|time_point|value_type)$"
|
||||
readability-identifier-naming.TypedefIgnoredRegexp: "^(const_iterator|const_reference|difference_type|duration|iterator|iterator_category|key_type|mapped_type|period|pointer|reference|rep|result_type|size_type|time_point|value_type)$"
|
||||
readability-identifier-naming.EnumCase: CamelCase
|
||||
readability-identifier-naming.EnumConstantCase: CamelCase
|
||||
readability-identifier-naming.ScopedEnumConstantCase: CamelCase
|
||||
@@ -122,5 +129,5 @@ CheckOptions:
|
||||
readability-identifier-naming.GlobalFunctionIgnoredRegexp: "^(to_string|hash_append|tuple_hash)$"
|
||||
|
||||
HeaderFilterRegex: '^.*/(tests?|xrpl|xrpld)/.*\.(h|hpp|ipp)$'
|
||||
ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$'
|
||||
ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$|^.*\.pb\.h$'
|
||||
WarningsAsErrors: "*"
|
||||
@@ -83,6 +83,7 @@ words:
|
||||
- coeffs
|
||||
- coldwallet
|
||||
- compr
|
||||
- cppcoro
|
||||
- conanfile
|
||||
- conanrun
|
||||
- confs
|
||||
@@ -129,6 +130,7 @@ words:
|
||||
- fsanitize
|
||||
- funclets
|
||||
- Gamal
|
||||
- gantt
|
||||
- gcov
|
||||
- gcovr
|
||||
- ghead
|
||||
@@ -178,6 +180,7 @@ words:
|
||||
- ltype
|
||||
- mathbunnyru
|
||||
- mcmodel
|
||||
- Mankawde
|
||||
- MEMORYSTATUSEX
|
||||
- MPTAMM
|
||||
- MPTDEX
|
||||
@@ -247,6 +250,7 @@ words:
|
||||
- protobuf
|
||||
- protos
|
||||
- ptrs
|
||||
- Pratik
|
||||
- pushd
|
||||
- pyenv
|
||||
- pyparsing
|
||||
@@ -258,6 +262,8 @@ words:
|
||||
- replayer
|
||||
- repodata
|
||||
- repomd
|
||||
- repost
|
||||
- reposts
|
||||
- rerandomize
|
||||
- rerandomization
|
||||
- rerandomized
|
||||
@@ -305,6 +311,7 @@ words:
|
||||
- sponsees
|
||||
- SRPMS
|
||||
- sslws
|
||||
- stackful
|
||||
- statsd
|
||||
- STATSDCOLLECTOR
|
||||
- stissue
|
||||
|
||||
@@ -15,30 +15,25 @@ outputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# A tag names its own version. Anything else takes it from BuildInfo.cpp and
|
||||
# appends the commit hash as build metadata, joined with a plus sign because a
|
||||
# Conan version cannot contain two hyphens.
|
||||
# A tag names its own version. Anything else is a development build named by
|
||||
# its commit hash, matching what cmake/XrplVersion.cmake derives: the head of
|
||||
# a pull request rather than the merge commit GitHub creates for it.
|
||||
- name: Determine version
|
||||
id: version
|
||||
shell: bash
|
||||
env:
|
||||
IS_TAG: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
SHA: ${{ github.sha }}
|
||||
SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
run: |
|
||||
if [[ "${IS_TAG}" == "true" ]]; then
|
||||
version="${REF_NAME}"
|
||||
else
|
||||
version="$(awk -F'"' '/versionString =/ { print $2 }' src/libxrpl/protocol/BuildInfo.cpp)"
|
||||
if [[ -z "${version}" ]]; then
|
||||
echo "Unable to read versionString from BuildInfo.cpp." >&2
|
||||
exit 1
|
||||
fi
|
||||
version="${version}+${SHA:0:7}"
|
||||
version="0.0.0-dev+${SHA:0:7}"
|
||||
fi
|
||||
|
||||
echo "version=${version}" | tee -a "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Determine release channel and package release
|
||||
id: release_info
|
||||
uses: XRPLF/actions/release-info@7cc0e4a8d9d0b838f92c48d312856b190341bbba
|
||||
uses: XRPLF/actions/release-info@ebcf6cea14eee258697308a51fea55cad777581b
|
||||
@@ -194,6 +194,9 @@ tests.libxrpl > xrpl.resource
|
||||
tests.libxrpl > xrpl.server
|
||||
tests.libxrpl > xrpl.shamap
|
||||
tests.libxrpl > xrpl.tx
|
||||
tests.xrpld > xrpld.rpc
|
||||
tests.xrpld > xrpl.json
|
||||
tests.xrpld > xrpl.protocol
|
||||
xrpl.conditions > xrpl.basics
|
||||
xrpl.conditions > xrpl.protocol
|
||||
xrpl.config > xrpl.basics
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit the script as soon as an error occurs.
|
||||
set -euo pipefail
|
||||
|
||||
# This script fails if the commits in <base>..<head> copy commits from a release or staging branch
|
||||
# (e.g. by rebasing or cherry-picking them) instead of merging that branch, see RELEASING.md.
|
||||
# Commits are compared by patch-id,
|
||||
# and only against release and staging commits that <head> does not already contain.
|
||||
# Usage: .github/scripts/releasing/check-no-copied-release-commits.sh <base> <head>
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Usage: $0 <base> <head>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BASE=$1
|
||||
HEAD=$2
|
||||
|
||||
patch_ids() {
|
||||
git log --no-merges --patch --no-color --no-ext-diff "$@" | git patch-id --stable | sort
|
||||
}
|
||||
|
||||
mapfile -t BRANCHES < <(git for-each-ref --format='%(refname)' 'refs/remotes/*/release/*' 'refs/remotes/*/staging/*')
|
||||
if [ "${#BRANCHES[@]}" -eq 0 ]; then
|
||||
echo "Error: No release or staging branches found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Each line is "<patch-id> <commit>".
|
||||
RELEASE_PATCHES=$(patch_ids "${BRANCHES[@]}" --not "${HEAD}")
|
||||
PR_PATCHES=$(patch_ids "${BASE}..${HEAD}")
|
||||
|
||||
# Each line is "<patch-id> <release commit> <PR commit>".
|
||||
COPIES=$(join <(echo "${RELEASE_PATCHES}") <(echo "${PR_PATCHES}"))
|
||||
if [ -z "${COPIES}" ]; then
|
||||
echo "No copied release commits found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "These commits copy release commits instead of merging them:"
|
||||
while read -r _ RELEASE_COMMIT PR_COMMIT; do
|
||||
echo " $(git log -1 --format='%h %s' "${PR_COMMIT}") (copies ${RELEASE_COMMIT:0:10})"
|
||||
done <<<"${COPIES}"
|
||||
echo
|
||||
echo "Merge the release tag (or branch) instead, see RELEASING.md."
|
||||
exit 1
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit the script as soon as an error occurs.
|
||||
set -euo pipefail
|
||||
|
||||
# This script fails if a final release (a tag like 3.4.0) on a release branch
|
||||
# is not merged back into <develop> within a few days, see RELEASING.md.
|
||||
# Usage: .github/scripts/releasing/check-releases-merged.sh <develop>
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <develop>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEVELOP=$1
|
||||
GRACE_DAYS=3
|
||||
|
||||
mapfile -t MERGED_ARGS < <(git for-each-ref --format='--merged=%(refname)' 'refs/remotes/*/release/*')
|
||||
if [ "${#MERGED_ARGS[@]}" -eq 0 ]; then
|
||||
echo "Error: No release branches found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Tags on a release branch that <develop> does not contain.
|
||||
TAGS=$(git for-each-ref --format='%(refname:short) %(creatordate:unix)' \
|
||||
"${MERGED_ARGS[@]}" --no-merged="${DEVELOP}" 'refs/tags/[0-9]*')
|
||||
|
||||
MISSING=0
|
||||
while read -r TAG CREATED; do
|
||||
if ! [[ "${TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
continue
|
||||
fi
|
||||
if (($(date +%s) - CREATED < GRACE_DAYS * 86400)); then
|
||||
echo "${TAG}: not merged yet, still within the ${GRACE_DAYS}-day grace period."
|
||||
else
|
||||
echo "${TAG}: not merged into develop."
|
||||
MISSING=1
|
||||
fi
|
||||
done <<<"${TAGS}"
|
||||
|
||||
if [ "${MISSING}" -ne 0 ]; then
|
||||
echo
|
||||
echo "Merge the missing releases back into develop, see RELEASING.md."
|
||||
exit 1
|
||||
fi
|
||||
echo "No releases past the grace period are missing from develop."
|
||||
@@ -74,7 +74,7 @@
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON",
|
||||
"package": {
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-49cdc10"
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-3a2d19f"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -86,7 +86,7 @@
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON -Dassert=ON",
|
||||
"package": {
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-49cdc10",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-3a2d19f",
|
||||
"variant": "assert"
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,7 @@
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON",
|
||||
"package": {
|
||||
"type": "rpm",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-49cdc10"
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-3a2d19f"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
base_image: debian:bookworm
|
||||
- name: rhel
|
||||
base_image: registry.access.redhat.com/ubi9/ubi:latest
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@696384b292577293292daed06af0306d1b83bd7d
|
||||
with:
|
||||
image_name: xrpld/nix-${{ matrix.distro.name }}
|
||||
dockerfile: nix/docker/Dockerfile
|
||||
|
||||
@@ -7,12 +7,12 @@ on:
|
||||
paths:
|
||||
- ".github/workflows/build-packaging-images.yml"
|
||||
- "bin/install-packaging-tools.sh"
|
||||
- "package/docker/**"
|
||||
- "package/images/packaging/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/build-packaging-images.yml"
|
||||
- "bin/install-packaging-tools.sh"
|
||||
- "package/docker/**"
|
||||
- "package/images/packaging/**"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
@@ -41,9 +41,9 @@ jobs:
|
||||
# AlmaLinux rather than UBI, which does not ship rpm-sign.
|
||||
- name: rhel
|
||||
base_image: almalinux:10
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@696384b292577293292daed06af0306d1b83bd7d
|
||||
with:
|
||||
image_name: xrpld/packaging-${{ matrix.distro.name }}
|
||||
dockerfile: package/docker/Dockerfile
|
||||
dockerfile: package/images/packaging/Dockerfile
|
||||
base_image: ${{ matrix.distro.base_image }}
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@696384b292577293292daed06af0306d1b83bd7d
|
||||
with:
|
||||
image_name: xrpld/pre-commit
|
||||
dockerfile: bin/pre-commit/Dockerfile
|
||||
|
||||
@@ -13,7 +13,6 @@ on:
|
||||
- ready_for_review
|
||||
branches:
|
||||
- develop
|
||||
- "release-*"
|
||||
- "release/*"
|
||||
- "staging/*"
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ on:
|
||||
- ready_for_review
|
||||
branches:
|
||||
- develop
|
||||
- "release-*"
|
||||
- "release/*"
|
||||
- "staging/*"
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# This workflow checks that every final release on a release branch
|
||||
# has been merged back into develop, see RELEASING.md.
|
||||
name: Check releases merged
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 06:47 UTC every Monday.
|
||||
- cron: "47 6 * * 1"
|
||||
push:
|
||||
branches:
|
||||
- "develop"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
OUTPUT_FILE: /tmp/releases-merged.txt
|
||||
ISSUE_FILE: /tmp/releases-merged-issue.md
|
||||
|
||||
jobs:
|
||||
releases-merged:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to open an issue on scheduled failures.
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
# The check needs the full history of develop, the release branches and tags.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check releases are merged into develop
|
||||
run: |
|
||||
set -o pipefail
|
||||
.github/scripts/releasing/check-releases-merged.sh origin/develop | tee "${OUTPUT_FILE}"
|
||||
|
||||
- name: Prepare issue body
|
||||
if: ${{ failure() && github.event_name == 'schedule' && github.repository == 'XRPLF/rippled' }}
|
||||
run: |
|
||||
{
|
||||
echo '```'
|
||||
cat "${OUTPUT_FILE}"
|
||||
echo '```'
|
||||
} >"${ISSUE_FILE}"
|
||||
|
||||
- name: Create issue
|
||||
if: ${{ failure() && github.event_name == 'schedule' && github.repository == 'XRPLF/rippled' }}
|
||||
uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712
|
||||
with:
|
||||
title: "Releases not merged back into develop"
|
||||
body_file: ${{ env.ISSUE_FILE }}
|
||||
labels: "Bug"
|
||||
@@ -150,6 +150,27 @@ jobs:
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
uses: ./.github/workflows/reusable-check-rename.yml
|
||||
|
||||
# Runs regardless of the changed files.
|
||||
# PRs into staging branches are skipped, since fixes may be cherry-picked between release lines.
|
||||
check-release-commits:
|
||||
needs: should-run
|
||||
if: ${{ github.event.pull_request.base.ref == 'develop' || github.event.merge_group.base_ref == 'refs/heads/develop' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
# The check needs the full history of the release and staging branches.
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Check for copied release commits
|
||||
env:
|
||||
BASE: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
|
||||
HEAD: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }}
|
||||
run: .github/scripts/releasing/check-no-copied-release-commits.sh "${BASE}" "${HEAD}"
|
||||
|
||||
clang-tidy:
|
||||
needs: should-run
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
@@ -230,6 +251,7 @@ jobs:
|
||||
- check-autogen
|
||||
- check-levelization
|
||||
- check-rename
|
||||
- check-release-commits
|
||||
- clang-tidy
|
||||
- build-test
|
||||
- rust
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# When a versioned tag is pushed, this workflow:
|
||||
#
|
||||
# - uploads the libxrpl recipe to the Conan remote
|
||||
# - builds and tests the release binaries
|
||||
# - uploads the libxrpl recipe to the Conan remote
|
||||
# - builds the DEB and RPM packages
|
||||
# - publishes those packages to the XRPLF package repositories
|
||||
#
|
||||
# Nothing is published unless the build passes, which is also where CMake
|
||||
# rejects a tag that is not a valid version, e.g. 3.2.01.
|
||||
name: Tag
|
||||
|
||||
on:
|
||||
@@ -22,6 +25,7 @@ defaults:
|
||||
jobs:
|
||||
upload-recipe:
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
needs: build-test
|
||||
uses: ./.github/workflows/reusable-upload-recipe.yml
|
||||
secrets:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
@@ -50,3 +54,7 @@ jobs:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
signing_key: ${{ secrets.NEXUS_PACKAGES_PRIVATE_KEY }}
|
||||
dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
antithesis_docker_host: ${{ secrets.ANTITHESIS_DOCKER_HOST }}
|
||||
antithesis_docker_path: ${{ secrets.ANTITHESIS_DOCKER_PATH }}
|
||||
antithesis_docker_credentials: ${{ secrets.ANTITHESIS_DOCKER_CREDENTIALS }}
|
||||
@@ -129,3 +129,7 @@ jobs:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
signing_key: ${{ secrets.NEXUS_PACKAGES_PRIVATE_KEY }}
|
||||
dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
antithesis_docker_host: ${{ secrets.ANTITHESIS_DOCKER_HOST }}
|
||||
antithesis_docker_path: ${{ secrets.ANTITHESIS_DOCKER_PATH }}
|
||||
antithesis_docker_credentials: ${{ secrets.ANTITHESIS_DOCKER_CREDENTIALS }}
|
||||
@@ -196,6 +196,19 @@ jobs:
|
||||
env:
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
CMAKE_ARGS: ${{ inputs.cmake_args }}
|
||||
# GitHub creates a merge commit for a PR
|
||||
# https://www.kenmuse.com/blog/the-many-shas-of-a-github-pull-request/
|
||||
#
|
||||
# We:
|
||||
# - explicitly provide branch name
|
||||
# - use `github.event.pull_request.head.sha` to get the SHA of last commit in the PR branch
|
||||
#
|
||||
# This way it works both for PRs and pushes to branches.
|
||||
GITHUB_BRANCH_NAME: "${{ github.head_ref || github.ref_name }}"
|
||||
GITHUB_HEAD_SHA: "${{ github.event.pull_request.head.sha || github.sha }}"
|
||||
#
|
||||
# If tag is being pushed, we use that version.
|
||||
FORCE_XRPLD_VERSION: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || '' }}
|
||||
run: |
|
||||
cmake \
|
||||
-G '${{ runner.os == 'Windows' && 'Visual Studio 18 2026' || 'Ninja' }}' \
|
||||
@@ -257,6 +270,7 @@ jobs:
|
||||
run: |
|
||||
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpld"
|
||||
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpl_tests"
|
||||
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpld_tests"
|
||||
|
||||
- name: Show ccache statistics
|
||||
if: ${{ inputs.ccache_enabled }}
|
||||
@@ -338,7 +352,9 @@ jobs:
|
||||
- name: Run the separate tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
|
||||
run: ./xrpl_tests
|
||||
run: |
|
||||
./xrpl_tests
|
||||
./xrpld_tests
|
||||
|
||||
- name: Run the embedded tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Build, verify and publish Linux packages from the pre-built xrpld and
|
||||
# validator-keys artifacts, in three stages:
|
||||
# validator-keys artifacts, in these stages:
|
||||
#
|
||||
# - 'package' builds and signs one format per config that carries a "package"
|
||||
# map in linux.json; that map names the container image and the format
|
||||
@@ -9,6 +9,9 @@
|
||||
# never reaches Nexus
|
||||
# - 'publish' uploads with the image's publish_pkg.py, doing a --dry-run
|
||||
# unless 'publish: true'
|
||||
# - 'docker' builds an Ubuntu image from the tested DEB, and one with the
|
||||
# voidstar binary for Antithesis, pushing them to Docker Hub and to the
|
||||
# Antithesis registry only with 'publish: true'
|
||||
#
|
||||
# Only linux/amd64 is supported; the runner is hardcoded in the jobs below.
|
||||
name: Package
|
||||
@@ -35,7 +38,19 @@ on:
|
||||
description: "The password or token for that Nexus account."
|
||||
required: false
|
||||
signing_key:
|
||||
description: "Armoured PGP private key used to sign the RPMs. Required when publishing."
|
||||
description: "Armoured PGP private key used to sign the RPMs."
|
||||
required: false
|
||||
dockerhub_token:
|
||||
description: "A Docker Hub organization access token for xrplf, with push access to xrplf/xrpld."
|
||||
required: false
|
||||
antithesis_docker_host:
|
||||
description: "The host of the Antithesis container registry, e.g. us-central1-docker.pkg.dev."
|
||||
required: false
|
||||
antithesis_docker_path:
|
||||
description: "The repository path in that registry, the image name excluded."
|
||||
required: false
|
||||
antithesis_docker_credentials:
|
||||
description: "The JSON key of a service account with push access to that repository."
|
||||
required: false
|
||||
|
||||
defaults:
|
||||
@@ -198,9 +213,6 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
# The name says which of the two this is, because the job runs either way:
|
||||
# with publish false it passes --dry-run and uploads nothing, and a job
|
||||
# called "publish ..." succeeding on a pull request reads like a release.
|
||||
name: "publish ${{ matrix.xrpld_artifact_name }}${{ !inputs.publish && ' (dry run)' || '' }}"
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -242,3 +254,86 @@ jobs:
|
||||
--package-dir "${PACKAGE_DIR}" \
|
||||
--nexus-url "${NEXUS_URL}" \
|
||||
${DRY_RUN_OPTION}
|
||||
|
||||
docker:
|
||||
needs: [test-install-deb, test-install-rpm]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [xrpld, voidstar]
|
||||
name: "docker ${{ matrix.target }}${{ !inputs.publish && ' (dry run)' || '' }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
CONTEXT: image-context
|
||||
IMAGE: ${{ matrix.target == 'voidstar' && 'xrpld-voidstar' || 'xrplf/xrpld' }}:${{ github.ref_type == 'tag' && github.ref_name || 'develop' }}
|
||||
# Docker Hub is public, so a private build never reaches it;
|
||||
# the Antithesis registry is not.
|
||||
PUSH: ${{ inputs.publish && (matrix.target == 'voidstar' || github.event.repository.visibility == 'public') }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Download package artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: "*-pkg"
|
||||
merge-multiple: true
|
||||
path: ${{ env.PACKAGE_DIR }}
|
||||
|
||||
- name: Download voidstar binary
|
||||
if: ${{ matrix.target == 'voidstar' }}
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: xrpld-ubuntu-clang-debug-amd64-voidstar
|
||||
path: ${{ env.CONTEXT }}
|
||||
|
||||
- name: Build image
|
||||
env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
run: |
|
||||
mkdir -p "${CONTEXT}"
|
||||
find "${PACKAGE_DIR}" -type f -name 'xrpld_[0-9]*.deb' -exec cp {} "${CONTEXT}/" \;
|
||||
docker build --pull --file package/images/xrpld/Dockerfile --target "${TARGET}" --tag "${IMAGE}" "${CONTEXT}"
|
||||
|
||||
- name: Start the server
|
||||
run: |
|
||||
container="$(docker run --detach "${IMAGE}" --standalone --silent --conf /etc/xrpld/xrpld.cfg)"
|
||||
trap 'docker rm --force "${container}" >/dev/null' EXIT
|
||||
|
||||
for _ in $(seq 30); do
|
||||
output="$(docker exec "${container}" xrpld --conf /etc/xrpld/xrpld.cfg server_info || true)"
|
||||
if [[ "${output}" == *'"status" : "success"'* ]]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
docker logs "${container}"
|
||||
exit 1
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
if: ${{ env.PUSH == 'true' && matrix.target == 'xrpld' }}
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: xrplf
|
||||
password: ${{ secrets.dockerhub_token }}
|
||||
|
||||
- name: Log in to the Antithesis registry
|
||||
if: ${{ env.PUSH == 'true' && matrix.target == 'voidstar' }}
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ${{ secrets.antithesis_docker_host }}
|
||||
username: _json_key
|
||||
password: ${{ secrets.antithesis_docker_credentials }}
|
||||
|
||||
- name: Push image
|
||||
if: ${{ env.PUSH == 'true' }}
|
||||
env:
|
||||
REGISTRY: ${{ matrix.target == 'voidstar' && format('{0}/{1}/', secrets.antithesis_docker_host, secrets.antithesis_docker_path) || '' }}
|
||||
run: |
|
||||
docker tag "${IMAGE}" "${REGISTRY}${IMAGE}"
|
||||
docker push "${REGISTRY}${IMAGE}"
|
||||
@@ -68,33 +68,17 @@ jobs:
|
||||
run: conan remote login "${REMOTE_NAME}" "${CONAN_LOGIN_USERNAME_XRPLF}" --password "${CONAN_PASSWORD_XRPLF}"
|
||||
|
||||
- name: Upload Conan recipe (version)
|
||||
env:
|
||||
VERSION: ${{ steps.release_info.outputs.version }}
|
||||
run: |
|
||||
conan export . --version=${{ steps.release_info.outputs.version }}
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.release_info.outputs.version }}
|
||||
conan export . --version="${VERSION}"
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" "xrpl/${VERSION}"
|
||||
|
||||
# When this workflow is triggered by a push event, it will always be when merging into the
|
||||
# 'develop' branch, see on-trigger.yml.
|
||||
- name: Upload Conan recipe (develop)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
run: |
|
||||
conan export . --version=develop
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/develop
|
||||
|
||||
# When this workflow is triggered by a pull request event, it will always be when merging into
|
||||
# one of the 'release' branches, see on-pr.yml.
|
||||
- name: Upload Conan recipe (rc)
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
conan export . --version=rc
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/rc
|
||||
|
||||
# When this workflow is triggered by a push event, it will always be when tagging a final
|
||||
# release, see on-tag.yml.
|
||||
- name: Upload Conan recipe (release)
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
run: |
|
||||
conan export . --version=release
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/release
|
||||
|
||||
outputs:
|
||||
ref: xrpl/${{ steps.release_info.outputs.version }}
|
||||
@@ -60,7 +60,7 @@ repos:
|
||||
types_or: [c++, c]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: f4d7745e17a28aad7eed2f4874ca8d1568c11c4c # frozen: v22.1.8
|
||||
rev: e2b496dc2bd8340c2524cb9a2d2a943cde1bb6df # frozen: v23.1.1
|
||||
hooks:
|
||||
- id: clang-format
|
||||
args: [--style=file]
|
||||
@@ -82,12 +82,12 @@ repos:
|
||||
files: ^crates/.*\.rs$
|
||||
|
||||
- repo: https://github.com/BlankSpruce/gersemi-pre-commit
|
||||
rev: e98930bdc210d3387007f9252d8c1694ea7e410f # frozen: 0.27.7
|
||||
rev: f1c4833f8cf23c6d952673abc73525411a5719e8 # frozen: 0.29.1
|
||||
hooks:
|
||||
- id: gersemi
|
||||
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: 9337a74165b178ae2c766f60bee7252a0f06f3e8 # frozen: v3.9.5
|
||||
rev: ef4a397f916211b4a39ccf9d3d9cbb6562157251 # frozen: v3.9.9
|
||||
hooks:
|
||||
- id: prettier
|
||||
args: [--end-of-line=auto]
|
||||
@@ -95,7 +95,7 @@ repos:
|
||||
# Scoped to package/: the rest of the repo's Python has pre-existing findings,
|
||||
# so widening these is its own change.
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: 7c55798a78262d14b2074abf623d8a992ebb70d4 # frozen: v0.16.2
|
||||
rev: a56c0b927e6465d37cae3e97d35d4d18ab2b96cd # frozen: v0.16.9
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: [--fix]
|
||||
@@ -107,14 +107,14 @@ repos:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: 41e691678310dfd3833f7ab4e180ddb014310356 # frozen: v2.3.0
|
||||
rev: 7ff8d35ae36a7d2b968f2f90b4c723e292e594ee # frozen: v2.3.1
|
||||
hooks:
|
||||
- id: mypy
|
||||
args: [--strict]
|
||||
files: ^package/.*\.py$
|
||||
|
||||
- repo: https://github.com/scop/pre-commit-shfmt
|
||||
rev: 05c1426671b9237fb5e1444dd63aa5731bec0dfb # frozen: v3.13.1-1
|
||||
rev: 479be5958357ba5ab65ce47172117348516860e9 # frozen: v3.14.1-1
|
||||
hooks:
|
||||
- id: shfmt
|
||||
args: [--write, --indent=4, --case-indent=true]
|
||||
@@ -133,7 +133,7 @@ repos:
|
||||
files: \.md$
|
||||
|
||||
- repo: https://github.com/streetsidesoftware/cspell-cli
|
||||
rev: ea11f9efc0bec520073405bc30552da887ba71bc # frozen: v10.0.1
|
||||
rev: f5c5d72342f35643988a9ffa0705c05bd3ff8383 # frozen: v10.3.0
|
||||
hooks:
|
||||
- id: cspell
|
||||
name: check changed files spelling
|
||||
|
||||
@@ -51,6 +51,12 @@ git checkout release/3.2.x
|
||||
For a stable release, choose one of the [tagged
|
||||
releases](https://github.com/XRPLF/rippled/releases).
|
||||
|
||||
A build reports `0.0.0-dev` with its short commit hash as build metadata, e.g.
|
||||
`0.0.0-dev+0123abc`. Only builds of a release, from a tag in CI or a versioned
|
||||
Conan reference, report a release version. To use another version, set the
|
||||
`FORCE_XRPLD_VERSION` environment variable when running CMake, e.g.
|
||||
`FORCE_XRPLD_VERSION=3.4.0`.
|
||||
|
||||
### Set Up Conan
|
||||
|
||||
Once your [development environment](./docs/build/environment.md) is ready, set
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,142 @@
|
||||
# Boost.Coroutine to C++20 Migration — Task List
|
||||
|
||||
> Parent document: [BoostToStdCoroutineSwitchPlan.md](BoostToStdCoroutineSwitchPlan.md)
|
||||
|
||||
---
|
||||
|
||||
## Milestone 1: New Coroutine Primitives
|
||||
|
||||
- [ ] **1.1** Design `CoroTask<T>` class with `promise_type`
|
||||
- Define `promise_type` with `initial_suspend`, `final_suspend`, `unhandled_exception`, `return_value`/`return_void`
|
||||
- Implement `FinalAwaiter` for continuation support
|
||||
- Implement move-only RAII handle wrapper
|
||||
- Support both `CoroTask<T>` and `CoroTask<void>`
|
||||
|
||||
- [ ] **1.2** Design and implement `JobQueueAwaiter`
|
||||
- `await_suspend()` calls `jq_.addJob(type, name, [h]{ h.resume(); })`
|
||||
- Handle `addJob()` failure (shutdown) — resume with error flag or throw
|
||||
- Integrate `nSuspend_` counter increment/decrement
|
||||
|
||||
- [ ] **1.3** Implement `LocalValues` swap in new coroutine resume path
|
||||
- Before `handle.resume()`: save thread-local, install coroutine-local
|
||||
- After `handle.resume()` returns: restore thread-local
|
||||
- Ensure this works when coroutine migrates between threads
|
||||
|
||||
- [ ] **1.4** Add `postCoroTask()` template to `JobQueue`
|
||||
- Accept callable returning `CoroTask<void>`
|
||||
- Schedule initial execution on JobQueue (mirror `postCoro()` behavior)
|
||||
- Return a handle/shared_ptr for join/cancel
|
||||
|
||||
- [ ] **1.5** Write unit tests (`src/test/core/CoroTask_test.cpp`)
|
||||
- Test `CoroTask<void>` runs to completion
|
||||
- Test `CoroTask<int>` returns value
|
||||
- Test exception propagation across co_await
|
||||
- Test coroutine destruction before completion
|
||||
- Test `JobQueueAwaiter` schedules on correct thread
|
||||
- Test `LocalValue` isolation across 4+ coroutines
|
||||
- Test shutdown rejection (addJob returns false)
|
||||
- Test `correct_order` equivalent (yield → join → post → complete)
|
||||
- Test `incorrect_order` equivalent (post → yield → complete)
|
||||
- Test multiple sequential co_await points
|
||||
|
||||
- [ ] **1.6** Verify build on GCC 12+, Clang 16+
|
||||
- [ ] **1.7** Run ASAN + TSAN on new tests
|
||||
- [ ] **1.8** Run full `--unittest` suite (no regressions)
|
||||
- [ ] **1.9** Self-review and create PR #1
|
||||
|
||||
---
|
||||
|
||||
## Milestone 2: Entry Point Migration
|
||||
|
||||
- [ ] **2.1** Migrate `ServerHandler::onRequest()` (`ServerHandler.cpp:287`)
|
||||
- Replace `m_jobQueue.postCoro(jtCLIENT_RPC, ...)` with `postCoroTask()`
|
||||
- Update lambda to return `CoroTask<void>` (add `co_return`)
|
||||
- Update `processSession` to accept new coroutine type
|
||||
|
||||
- [ ] **2.2** Migrate `ServerHandler::onWSMessage()` (`ServerHandler.cpp:325`)
|
||||
- Replace `m_jobQueue.postCoro(jtCLIENT_WEBSOCKET, ...)` with `postCoroTask()`
|
||||
- Update lambda signature
|
||||
|
||||
- [ ] **2.3** Migrate `GRPCServer::CallData::process()` (`GRPCServer.cpp:102`)
|
||||
- Replace `app_.getJobQueue().postCoro(JobType::jtRPC, ...)` with `postCoroTask()`
|
||||
- Update `process(shared_ptr<Coro> coro)` overload signature
|
||||
|
||||
- [ ] **2.4** Update `RPC::Context` (`Context.h:27`)
|
||||
- Replace `std::shared_ptr<JobQueue::Coro> coro{}` with new coroutine wrapper type
|
||||
- Ensure all code that accesses `context.coro` compiles
|
||||
|
||||
- [ ] **2.5** Update `ServerHandler.h` signatures
|
||||
- `processSession()` and `processRequest()` parameter types
|
||||
|
||||
- [ ] **2.6** Update `GRPCServer.h` signatures
|
||||
- `process()` method parameter types
|
||||
|
||||
- [ ] **2.7** Run full `--unittest` suite
|
||||
- [ ] **2.8** Manual smoke test: HTTP + WS + gRPC RPC requests
|
||||
- [ ] **2.9** Run ASAN + TSAN
|
||||
- [ ] **2.10** Self-review and create PR #2
|
||||
|
||||
---
|
||||
|
||||
## Milestone 3: Handler Migration
|
||||
|
||||
- [ ] **3.1** Migrate `doRipplePathFind()` (`RipplePathFind.cpp`)
|
||||
- Replace `context.coro->yield()` with `co_await PathFindAwaiter{...}`
|
||||
- Replace continuation lambda's `coro->post()` / `coro->resume()` with awaiter scheduling
|
||||
- Handle shutdown case (post failure) in awaiter
|
||||
|
||||
- [ ] **3.2** Create `PathFindAwaiter` (or use generic `JobQueueAwaiter`)
|
||||
- Encapsulate the continuation + yield pattern from `RipplePathFind.cpp` lines 108-132
|
||||
|
||||
- [ ] **3.3** Update `Path_test.cpp`
|
||||
- Replace `postCoro` usage with `postCoroTask`
|
||||
- Ensure `context.coro` usage matches new type
|
||||
|
||||
- [ ] **3.4** Update `AMMTest.cpp`
|
||||
- Replace `postCoro` usage with `postCoroTask`
|
||||
|
||||
- [ ] **3.5** Rewrite `Coroutine_test.cpp` for new API
|
||||
- `correct_order`: postCoroTask → co_await → join → resume → complete
|
||||
- `incorrect_order`: post before yield equivalent
|
||||
- `thread_specific_storage`: 4 coroutines with LocalValue isolation
|
||||
|
||||
- [ ] **3.6** Update `JobQueue_test.cpp` `testPostCoro`
|
||||
- Migrate to `postCoroTask` API
|
||||
|
||||
- [ ] **3.7** Verify `ripple_path_find` works end-to-end with new coroutines
|
||||
- [ ] **3.8** Test shutdown-during-pathfind scenario
|
||||
- [ ] **3.9** Run full `--unittest` suite
|
||||
- [ ] **3.10** Run ASAN + TSAN
|
||||
- [ ] **3.11** Self-review and create PR #3
|
||||
|
||||
---
|
||||
|
||||
## Milestone 4: Cleanup & Validation
|
||||
|
||||
- [ ] **4.1** Delete `include/xrpl/core/Coro.ipp`
|
||||
- [ ] **4.2** Remove from `JobQueue.h`:
|
||||
- `#include <boost/coroutine2/all.hpp>`
|
||||
- `struct Coro_create_t`
|
||||
- `class Coro` (entire class)
|
||||
- `postCoro()` template
|
||||
- Comment block (lines 322-377) describing old race condition
|
||||
- [ ] **4.3** Update `cmake/deps/Boost.cmake`:
|
||||
- Remove `coroutine` from `find_package(Boost REQUIRED COMPONENTS ...)`
|
||||
- Remove `Boost::coroutine` from `target_link_libraries`
|
||||
- [ ] **4.4** Update `cmake/XrplInterface.cmake`:
|
||||
- Remove `BOOST_COROUTINES2_NO_DEPRECATION_WARNING`
|
||||
- [ ] **4.5** Run memory benchmark
|
||||
- Create N=1000 coroutines, compare RSS: before vs after
|
||||
- Document results
|
||||
- [ ] **4.6** Run context switch benchmark
|
||||
- 100K yield/resume cycles, compare latency: before vs after
|
||||
- Document results
|
||||
- [ ] **4.7** Run RPC throughput benchmark
|
||||
- Concurrent `ripple_path_find` requests, compare throughput
|
||||
- Document results
|
||||
- [ ] **4.8** Run full `--unittest` suite
|
||||
- [ ] **4.9** Run ASAN, TSAN, UBSan
|
||||
- Confirm `__asan_handle_no_return` warnings are gone
|
||||
- [ ] **4.10** Verify build on all supported compilers
|
||||
- [ ] **4.11** Self-review and create PR #4
|
||||
- [ ] **4.12** Document final benchmark results in PR description
|
||||
+2
-1
@@ -77,7 +77,6 @@ endif()
|
||||
include(PatchNixBinary)
|
||||
|
||||
include(XrplSanity)
|
||||
include(XrplVersion)
|
||||
include(XrplSettings)
|
||||
# this check has to remain in the top-level cmake because of the early return statement
|
||||
if(packages_only)
|
||||
@@ -115,6 +114,7 @@ find_package(secp256k1 REQUIRED)
|
||||
find_package(SOCI REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
find_package(xxHash REQUIRED)
|
||||
find_package(xrpl-rpc-spec REQUIRED)
|
||||
|
||||
target_link_libraries(
|
||||
xrpl_libs
|
||||
@@ -175,6 +175,7 @@ include(XrplPackaging)
|
||||
if(tests)
|
||||
include(CTest)
|
||||
add_subdirectory(src/tests/libxrpl)
|
||||
add_subdirectory(src/tests/xrpld)
|
||||
endif()
|
||||
|
||||
if(benchmark)
|
||||
|
||||
+6
-2
@@ -479,8 +479,12 @@ exists, then no other unit test will be executed, apart from `TestSuiteName`.
|
||||
elsewhere in the codebase.
|
||||
12. Use clear and self-explanatory names for functions, variables,
|
||||
structs and classes.
|
||||
13. Use TitleCase for classes, structs and filenames, camelCase for
|
||||
function and variable names, lower case for namespaces and folders.
|
||||
13. Use TitleCase for classes, structs, type aliases and filenames,
|
||||
camelCase for function and variable names, lower case for namespaces and
|
||||
folders. The exception is a type alias that generic code looks up by name
|
||||
(`value_type`, `iterator`, `result_type`, and the rest of the standard
|
||||
container, hash and clock members), which keeps its snake_case spelling;
|
||||
`.clang-tidy` lists the names that are allowed.
|
||||
14. Provide as many comments as you feel that a competent programmer
|
||||
would need to understand what your code does.
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ $# -ne 3 || "$1" == "--help" || "$1" = "-h" ]]; then
|
||||
name=$(basename $0)
|
||||
cat <<-USAGE
|
||||
Usage: $name workbranch base/branch version
|
||||
|
||||
* workbranch will be created locally from base/branch. If it exists,
|
||||
it will be reused, so make sure you don't overwrite any work.
|
||||
* base/branch may be specified as user:branch to allow easy copying
|
||||
from Github PRs.
|
||||
USAGE
|
||||
exit 0
|
||||
fi
|
||||
|
||||
work="$1"
|
||||
shift
|
||||
|
||||
base=$(echo "$1" | sed "s/:/\//")
|
||||
shift
|
||||
|
||||
version=$1
|
||||
shift
|
||||
|
||||
set -e
|
||||
|
||||
git fetch upstreams
|
||||
|
||||
git checkout -B "${work}" --no-track "${base}"
|
||||
|
||||
push=$(git rev-parse --abbrev-ref --symbolic-full-name '@{push}' \
|
||||
2>/dev/null) || true
|
||||
if [[ "${push}" != "" ]]; then
|
||||
echo "Warning: ${push} may already exist."
|
||||
fi
|
||||
|
||||
build=$(find -name BuildInfo.cpp)
|
||||
sed 's/\(^.*versionString =\).*$/\1 "'${version}'"/' ${build} >version.cpp &&
|
||||
diff "${build}" version.cpp && exit 1 ||
|
||||
mv -vi version.cpp ${build}
|
||||
|
||||
git diff
|
||||
|
||||
git add ${build}
|
||||
|
||||
git commit -S -m "Set version to ${version}"
|
||||
|
||||
git log --oneline --first-parent ${base}^..
|
||||
|
||||
cat <<PUSH
|
||||
|
||||
-------------------------------------------------------------------
|
||||
This script will not push. Verify everything is correct, then push
|
||||
to your repo, and create a PR as described in CONTRIBUTING.md.
|
||||
-------------------------------------------------------------------
|
||||
PUSH
|
||||
@@ -1,28 +0,0 @@
|
||||
include_guard()
|
||||
|
||||
set(GIT_BUILD_BRANCH "")
|
||||
set(GIT_COMMIT_HASH "")
|
||||
|
||||
find_package(Git)
|
||||
if(NOT Git_FOUND)
|
||||
message(WARNING "Git not found. Git branch and commit hash will be empty.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(GIT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.git)
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
${GIT_EXECUTABLE} --git-dir=${GIT_DIRECTORY} rev-parse --abbrev-ref HEAD
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
OUTPUT_VARIABLE GIT_BUILD_BRANCH
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} --git-dir=${GIT_DIRECTORY} rev-parse HEAD
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
||||
)
|
||||
|
||||
message(STATUS "Git branch: ${GIT_BUILD_BRANCH}")
|
||||
message(STATUS "Git commit hash: ${GIT_COMMIT_HASH}")
|
||||
+14
-2
@@ -81,7 +81,7 @@ include(target_link_modules)
|
||||
add_module(xrpl beast)
|
||||
target_link_libraries(xrpl.libxrpl.beast PUBLIC xrpl.imports.main)
|
||||
|
||||
include(GitInfo)
|
||||
include(XrplVersion)
|
||||
add_module(xrpl git)
|
||||
target_compile_definitions(
|
||||
xrpl.libxrpl.git
|
||||
@@ -111,6 +111,11 @@ target_link_libraries(
|
||||
xrpl.libxrpl.protocol
|
||||
PUBLIC xrpl.libxrpl.crypto xrpl.libxrpl.git xrpl.libxrpl.json
|
||||
)
|
||||
# Only on BuildInfo.cpp, so a new version does not rebuild the whole module.
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/libxrpl/protocol/BuildInfo.cpp
|
||||
PROPERTIES COMPILE_DEFINITIONS XRPLD_VERSION="${XRPLD_VERSION}"
|
||||
)
|
||||
|
||||
# Level 05
|
||||
add_module(xrpl protocol_autogen)
|
||||
@@ -296,7 +301,14 @@ if(xrpld)
|
||||
target_sources(xrpld PRIVATE ${sources})
|
||||
endif()
|
||||
|
||||
target_link_libraries(xrpld Xrpl::boost Xrpl::opts Xrpl::libs xrpl.libxrpl)
|
||||
target_link_libraries(
|
||||
xrpld
|
||||
Xrpl::boost
|
||||
Xrpl::opts
|
||||
Xrpl::libs
|
||||
xrpl.libxrpl
|
||||
rpcspec::rpcspec
|
||||
)
|
||||
exclude_if_included(xrpld)
|
||||
# define a macro for tests that might need to
|
||||
# be excluded or run differently in CI environment
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ setup_target_for_coverage_gcovr(
|
||||
"include/xrpl/beast/test"
|
||||
"include/xrpl/beast/unit_test"
|
||||
"${CMAKE_BINARY_DIR}/pb-xrpl.libpb"
|
||||
DEPENDENCIES xrpld xrpl_tests
|
||||
DEPENDENCIES xrpld xrpl_tests xrpld_tests
|
||||
)
|
||||
|
||||
add_code_coverage_to_target(opts INTERFACE)
|
||||
+73
-13
@@ -1,15 +1,75 @@
|
||||
#[===================================================================[
|
||||
read version from source
|
||||
#]===================================================================]
|
||||
find_package(Git)
|
||||
|
||||
file(STRINGS src/libxrpl/protocol/BuildInfo.cpp BUILD_INFO)
|
||||
foreach(line_ ${BUILD_INFO})
|
||||
if(line_ MATCHES "versionString[ ]*=[ ]*\"(.+)\"")
|
||||
set(xrpld_version ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
endforeach()
|
||||
if(xrpld_version)
|
||||
message(STATUS "xrpld version: ${xrpld_version}")
|
||||
else()
|
||||
message(FATAL_ERROR "unable to determine xrpld version")
|
||||
set(GIT_BUILD_BRANCH "")
|
||||
set(GIT_COMMIT_HASH "")
|
||||
|
||||
if(DEFINED ENV{GITHUB_BRANCH_NAME})
|
||||
set(GIT_BUILD_BRANCH $ENV{GITHUB_BRANCH_NAME})
|
||||
set(GIT_COMMIT_HASH $ENV{GITHUB_HEAD_SHA})
|
||||
elseif(Git_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/../.git")
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
|
||||
OUTPUT_VARIABLE GIT_BUILD_BRANCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
|
||||
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "Git branch: ${GIT_BUILD_BRANCH}")
|
||||
message(STATUS "Git commit hash: ${GIT_COMMIT_HASH}")
|
||||
|
||||
if(
|
||||
DEFINED ENV{FORCE_XRPLD_VERSION}
|
||||
AND NOT "$ENV{FORCE_XRPLD_VERSION}" STREQUAL ""
|
||||
)
|
||||
message(
|
||||
STATUS
|
||||
"Using explicitly provided '$ENV{FORCE_XRPLD_VERSION}' as xrpld version"
|
||||
)
|
||||
|
||||
set(XRPLD_VERSION "$ENV{FORCE_XRPLD_VERSION}")
|
||||
|
||||
# The rules beast::SemanticVersion::parse applies, so that an invalid version
|
||||
# fails here rather than when xrpld starts. It reads each number as an int.
|
||||
set(SEMVER_NUMBER "(0|[1-9][0-9]*)")
|
||||
set(SEMVER_PRE_RELEASE "[A-Za-z1-9-][A-Za-z0-9-]*")
|
||||
set(SEMVER_METADATA "[A-Za-z0-9-]+")
|
||||
set(SEMVER_NUMBER_MAX 2147483647)
|
||||
if(
|
||||
NOT XRPLD_VERSION
|
||||
MATCHES
|
||||
"^${SEMVER_NUMBER}\\.${SEMVER_NUMBER}\\.${SEMVER_NUMBER}(-${SEMVER_PRE_RELEASE}(\\.${SEMVER_PRE_RELEASE})*)?(\\+${SEMVER_METADATA}(\\.${SEMVER_METADATA})*)?$"
|
||||
OR CMAKE_MATCH_1 GREATER SEMVER_NUMBER_MAX
|
||||
OR CMAKE_MATCH_2 GREATER SEMVER_NUMBER_MAX
|
||||
OR CMAKE_MATCH_3 GREATER SEMVER_NUMBER_MAX
|
||||
)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"FORCE_XRPLD_VERSION '${XRPLD_VERSION}' is not a semantic version xrpld accepts, see https://semver.org"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Using '0.0.0-dev+<git short rev>' as xrpld version")
|
||||
|
||||
if(GIT_COMMIT_HASH STREQUAL "")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Unable to determine xrpld version without git, set FORCE_XRPLD_VERSION"
|
||||
)
|
||||
endif()
|
||||
|
||||
string(SUBSTRING ${GIT_COMMIT_HASH} 0 7 GIT_COMMIT_HASH_SHORT)
|
||||
|
||||
set(XRPLD_VERSION "0.0.0-dev+${GIT_COMMIT_HASH_SHORT}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Build version: ${XRPLD_VERSION}")
|
||||
@@ -59,7 +59,7 @@ def create_transaction_parser():
|
||||
# the members that differ from these.
|
||||
SETTING_DEFAULTS = {
|
||||
"delegable": "Delegation::NotDelegable",
|
||||
"amendment": "uint256{}",
|
||||
"amendment": "UInt256{}",
|
||||
"privileges": "Privilege::NoPriv",
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ ${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(requir
|
||||
* @return The constructed ledger entry wrapper.
|
||||
*/
|
||||
${name}
|
||||
build(uint256 const& index)
|
||||
build(UInt256 const& index)
|
||||
{
|
||||
return ${name}{std::make_shared<SLE>(std::move(object_), index)};
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace xrpl::ledger_entries {
|
||||
// builder's STObject and the wrapper's SLE.
|
||||
TEST(${name}Tests, BuilderSettersRoundTrip)
|
||||
{
|
||||
uint256 const index{1u};
|
||||
UInt256 const index{1u};
|
||||
|
||||
% for field in fields:
|
||||
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
||||
@@ -85,7 +85,7 @@ TEST(${name}Tests, BuilderSettersRoundTrip)
|
||||
// from that SLE, build a new wrapper, and verify all fields (and validate()).
|
||||
TEST(${name}Tests, BuilderFromSleRoundTrip)
|
||||
{
|
||||
uint256 const index{2u};
|
||||
UInt256 const index{2u};
|
||||
|
||||
% for field in fields:
|
||||
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
||||
@@ -146,7 +146,7 @@ TEST(${name}Tests, BuilderFromSleRoundTrip)
|
||||
// 3) Verify wrapper throws when constructed from wrong ledger entry type.
|
||||
TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
|
||||
{
|
||||
uint256 const index{3u};
|
||||
UInt256 const index{3u};
|
||||
|
||||
// Build a valid ledger entry of a different type
|
||||
// Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq
|
||||
@@ -177,7 +177,7 @@ TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
|
||||
// 4) Verify builder throws when constructed from wrong ledger entry type.
|
||||
TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
|
||||
{
|
||||
uint256 const index{4u};
|
||||
UInt256 const index{4u};
|
||||
|
||||
// Build a valid ledger entry of a different type
|
||||
% if wrong_le_include == "Ticket":
|
||||
@@ -207,7 +207,7 @@ TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
|
||||
// 5) Build with only required fields and verify optional fields return nullopt.
|
||||
TEST(${name}Tests, OptionalFieldsReturnNullopt)
|
||||
{
|
||||
uint256 const index{3u};
|
||||
UInt256 const index{3u};
|
||||
|
||||
% for field in required_fields:
|
||||
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
||||
|
||||
+7
-2
@@ -3,6 +3,7 @@
|
||||
"requires": [
|
||||
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
|
||||
"xrpl-rpc-spec/0.1.19#870b2d3abcfbbf13b61c2d3c69495060%1790348286.187549",
|
||||
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
|
||||
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
|
||||
@@ -19,8 +20,8 @@
|
||||
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1782392403.066892",
|
||||
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
|
||||
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979",
|
||||
"grpc/1.81.1#f729f6d75992d20f9c72828e9142d62f%1783945160.094135",
|
||||
"fast_float/8.2.10#f6f28d6bb22112078e7dbda611caf681%1782494504.298",
|
||||
"grpc/1.81.1#b87796a4269034856cbc1a2522db16eb%1788275071.530512",
|
||||
"fast_float/8.2.10#f6f28d6bb22112078e7dbda611caf681%1785888854.601666",
|
||||
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
|
||||
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
|
||||
"corrosion/0.6.1#bfa292df0a957bc70a450ff316cd9435%1786119416.131296",
|
||||
@@ -45,6 +46,10 @@
|
||||
],
|
||||
"python_requires": [],
|
||||
"overrides": {
|
||||
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c": [
|
||||
null,
|
||||
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c"
|
||||
],
|
||||
"protobuf/[>=5.27.0 <7]": [
|
||||
"protobuf/6.33.5"
|
||||
],
|
||||
|
||||
+18
-7
@@ -1,9 +1,13 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
from conan.tools.env import Environment
|
||||
|
||||
from conan import ConanFile
|
||||
|
||||
DEV_VERSION = "0.0.0-dev"
|
||||
|
||||
|
||||
class Xrpl(ConanFile):
|
||||
name = "xrpl"
|
||||
@@ -36,6 +40,7 @@ class Xrpl(ConanFile):
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.6.3",
|
||||
"soci/4.0.3",
|
||||
"xrpl-rpc-spec/0.1.19",
|
||||
"zlib/1.3.2",
|
||||
]
|
||||
|
||||
@@ -114,17 +119,12 @@ class Xrpl(ConanFile):
|
||||
"soci/*:shared": False,
|
||||
"soci/*:with_sqlite3": True,
|
||||
"soci/*:with_boost": True,
|
||||
"xrpl-rpc-spec/*:server": "xrpld",
|
||||
"xxhash/*:shared": False,
|
||||
}
|
||||
|
||||
def set_version(self):
|
||||
if self.version is None:
|
||||
path = f"{self.recipe_folder}/src/libxrpl/protocol/BuildInfo.cpp"
|
||||
regex = r"versionString\s?=\s?\"(.*)\""
|
||||
with open(path, encoding="utf-8") as file:
|
||||
matches = (re.search(regex, line) for line in file)
|
||||
match = next(m for m in matches if m)
|
||||
self.version = match.group(1)
|
||||
self.version = self.version or DEV_VERSION
|
||||
|
||||
def configure(self):
|
||||
if self.settings.compiler == "apple-clang":
|
||||
@@ -167,6 +167,17 @@ class Xrpl(ConanFile):
|
||||
generators = "CMakeDeps"
|
||||
|
||||
def generate(self):
|
||||
# The sources in the Conan cache have no git history, so the version
|
||||
# comes from the reference, unless it is not one, like 'develop'.
|
||||
if not os.path.exists(os.path.join(self.source_folder, ".git")):
|
||||
version = str(self.version)
|
||||
env = Environment()
|
||||
env.define(
|
||||
"FORCE_XRPLD_VERSION",
|
||||
version if re.match(r"\d+\.\d+\.\d+", version) else DEV_VERSION,
|
||||
)
|
||||
env.vars(self).save_script("xrpld_version")
|
||||
|
||||
tc = CMakeToolchain(self)
|
||||
tc.variables["tests"] = self.options.tests
|
||||
tc.variables["benchmark"] = self.options.benchmark
|
||||
|
||||
Generated
+10
-10
@@ -57,9 +57,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.199"
|
||||
version = "1.0.202"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824894a4a85dca76d4c95c2b9098c036f5a29f627b30c12780774f6654e60974"
|
||||
checksum = "13f6de320895f42e6e081abb5c7983bedcf0b6d0ff9323de0d33f620c8ac1199"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxx-build",
|
||||
@@ -72,9 +72,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.199"
|
||||
version = "1.0.202"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1ae0b651ea5b0000b19513aef5a03f194d7e3486f2d9258b658da8677fe9036"
|
||||
checksum = "4fde53ca86b9704a943fef0f1e1d836239a6aedca5de3c65fa9f97ec0bd46d39"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -87,9 +87,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-cmd"
|
||||
version = "1.0.199"
|
||||
version = "1.0.202"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb05f91d3fb8435d9bab6ac5ce6ac1868be774325fb7fb2a91be39393b21388e"
|
||||
checksum = "07bae89236c811fd4d08ed3441759ac4ac98d752cbfd3de341315ba16ad20ec3"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
@@ -101,15 +101,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.199"
|
||||
version = "1.0.202"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf293202e0e3e98495785745389e8d0755b217e66f19194a5c695c25e03282ef"
|
||||
checksum = "49045042e5fced01b80742aba5508de82aa4f13677ed3fa2b4cda709c40c5918"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.199"
|
||||
version = "1.0.202"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca001d746947c7249ed9d332a10f7a59daedbafeb0ec68c5c18a7db7a93f6ccc"
|
||||
checksum = "b181252e2e3d3b5d183afbdc033a3958b445eb3e1a0ae65fc3d4f5259f5da6fd"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
|
||||
+17
-3
@@ -1,5 +1,19 @@
|
||||
# `xrpld` Docker Image
|
||||
|
||||
- Some info relating to Docker containers can be found here: [../Builds/containers](../Builds/containers)
|
||||
- Images for building and testing xrpld can be found here: [thejohnfreeman/rippled-docker](https://github.com/thejohnfreeman/rippled-docker/)
|
||||
- These images do not have xrpld. They have all the tools necessary to build xrpld.
|
||||
`xrpld` is published to Docker Hub as [`xrplf/xrpld`](https://hub.docker.com/r/xrplf/xrpld):
|
||||
the `xrpld` DEB package installed on Ubuntu 26.04, running as the `xrpld` user.
|
||||
Each release is tagged with its version, `xrplf/xrpld:<version>`, and
|
||||
`xrplf/xrpld:develop` follows the `develop` branch.
|
||||
See [`package/README.md`](../package/README.md#docker-images) for how it is built
|
||||
and tagged.
|
||||
|
||||
```bash
|
||||
docker run --detach --name xrpld \
|
||||
--volume xrpld-db:/var/lib/xrpld \
|
||||
--publish 2459:2459 \
|
||||
xrplf/xrpld:develop
|
||||
```
|
||||
|
||||
The admin ports (5005, 6006 and 50051) listen on `127.0.0.1` in the shipped
|
||||
configuration; to reach them from outside the container, mount your own over
|
||||
`/etc/xrpld/xrpld.cfg`.
|
||||
@@ -0,0 +1,11 @@
|
||||
# basics' container adapters (TaggedCache, partitioned_unordered_map,
|
||||
# base_uint) model std concepts, which mandate
|
||||
# snake_case member type names.
|
||||
#
|
||||
# clang-tidy replaces rather than merges this option, so the list is the
|
||||
# top-level one restated plus the 6 name(s) needed only here:
|
||||
# allocator_type const_pointer const_reverse_iterator hasher key_equal reverse_iterator
|
||||
InheritParentConfig: true
|
||||
CheckOptions:
|
||||
readability-identifier-naming.TypeAliasIgnoredRegexp: "^(allocator_type|const_iterator|const_pointer|const_reference|const_reverse_iterator|difference_type|duration|hasher|iterator|iterator_category|key_equal|key_type|mapped_type|period|pointer|reference|rep|result_type|reverse_iterator|size_type|time_point|value_type)$"
|
||||
readability-identifier-naming.TypedefIgnoredRegexp: "^(allocator_type|const_iterator|const_pointer|const_reference|const_reverse_iterator|difference_type|duration|hasher|iterator|iterator_category|key_equal|key_type|mapped_type|period|pointer|reference|rep|result_type|reverse_iterator|size_type|time_point|value_type)$"
|
||||
@@ -618,13 +618,16 @@ SharedWeakUnion<T>::convertToWeak()
|
||||
unsafeSetRawPtr(nullptr);
|
||||
return true; // Should never happen
|
||||
// LCOV_EXCL_STOP
|
||||
case PartialDestroy:
|
||||
// This is a weird case. We just converted the last strong
|
||||
// pointer to a weak pointer.
|
||||
case PartialDestroy: {
|
||||
// We just converted the last strong pointer to a weak pointer.
|
||||
// The weak ref we now hold keeps the object from being fully
|
||||
// destroyed, so `p` stays valid; only the copy passed to
|
||||
// `partialDestructorFinished` is nulled.
|
||||
p->partialDestructor();
|
||||
partialDestructorFinished(&p);
|
||||
// p is null and may no longer be used
|
||||
auto finished = p;
|
||||
partialDestructorFinished(&finished);
|
||||
break;
|
||||
}
|
||||
}
|
||||
unsafeSetRawPtr(p, RefStrength::Weak);
|
||||
return true;
|
||||
|
||||
@@ -77,21 +77,13 @@ struct IntrusiveRefCounts
|
||||
std::size_t
|
||||
useCount() const noexcept;
|
||||
|
||||
// This function MUST be called after a partial destructor finishes running.
|
||||
// Calling this function may cause other threads to delete the object
|
||||
// pointed to by `o`, so `o` should never be used after calling this
|
||||
// function. The parameter will be set to a `nullptr` after calling this
|
||||
// function to emphasize that it should not be used.
|
||||
// Note: This is intentionally NOT called at the end of `partialDestructor`.
|
||||
// The reason for this is if new classes are written to support this smart
|
||||
// pointer class, they need to write their own `partialDestructor` function
|
||||
// and ensure `partialDestructorFinished` is called at the end. Putting this
|
||||
// call inside the smart pointer class itself is expected to be less error
|
||||
// prone.
|
||||
// Note: The "two-star" programming is intentional. It emphasizes that `o`
|
||||
// may be deleted and the unergonomic API is meant to signal the special
|
||||
// nature of this function call to callers.
|
||||
// Note: This is a template to support incompletely defined classes.
|
||||
// MUST be called after `partialDestructor` returns. Another thread may
|
||||
// then delete the object, so `*o` is nulled and must not be used after
|
||||
// (unless the caller holds its own weak ref, e.g.
|
||||
// SharedWeakUnion::convertToWeak). Called by the smart pointers, not
|
||||
// `partialDestructor`, so custom partial destructors can't forget it.
|
||||
// The two-star API signals that `*o` may be deleted. Templated to
|
||||
// support incomplete types.
|
||||
template <class T>
|
||||
friend void
|
||||
partialDestructorFinished(T** o);
|
||||
@@ -334,15 +326,11 @@ IntrusiveRefCounts::addWeakReleaseStrongRef() const
|
||||
ReleaseStrongRefAction action = NoOp;
|
||||
if (prevVal.strong == 1)
|
||||
{
|
||||
if (prevVal.weak == 0)
|
||||
{
|
||||
action = NoOp;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextIntVal |= kPartialDestroyStartedMask;
|
||||
action = PartialDestroy;
|
||||
}
|
||||
// The weak ref added here keeps the weak count non-zero, so
|
||||
// releasing the last strong ref always starts a partial destroy,
|
||||
// regardless of the previous weak count.
|
||||
nextIntVal |= kPartialDestroyStartedMask;
|
||||
action = PartialDestroy;
|
||||
}
|
||||
if (refCounts_.compare_exchange_weak(prevIntVal, nextIntVal, std::memory_order_acq_rel))
|
||||
{
|
||||
@@ -358,24 +346,26 @@ IntrusiveRefCounts::addWeakReleaseStrongRef() const
|
||||
inline ReleaseWeakRefAction
|
||||
IntrusiveRefCounts::releaseWeakRef() const
|
||||
{
|
||||
auto prevIntVal = refCounts_.fetch_sub(kWeakDelta, std::memory_order_acq_rel);
|
||||
RefCountPair prev = prevIntVal;
|
||||
auto const prevIntVal = refCounts_.fetch_sub(kWeakDelta, std::memory_order_acq_rel);
|
||||
RefCountPair const prev = prevIntVal;
|
||||
if (prev.weak == 1 && prev.strong == 0)
|
||||
{
|
||||
// `wait` blocks while the value equals its argument, so it must be
|
||||
// given the value as it is after the decrement above.
|
||||
auto curIntVal = prevIntVal - kWeakDelta;
|
||||
if (prev.partialDestroyStartedBit == 0u)
|
||||
{
|
||||
// This case should only be hit if the partialDestroyStartedBit is
|
||||
// set non-atomically (and even then very rarely). The code is kept
|
||||
// in case we need to set the flag non-atomically for perf reasons.
|
||||
refCounts_.wait(prevIntVal, std::memory_order_acquire);
|
||||
prevIntVal = refCounts_.load(std::memory_order_acquire);
|
||||
prev = RefCountPair{prevIntVal};
|
||||
refCounts_.wait(curIntVal, std::memory_order_acquire);
|
||||
curIntVal = refCounts_.load(std::memory_order_acquire);
|
||||
}
|
||||
if (prev.partialDestroyFinishedBit == 0u)
|
||||
if (RefCountPair{curIntVal}.partialDestroyFinishedBit == 0u)
|
||||
{
|
||||
// partial destroy MUST finish before running a full destroy (when
|
||||
// using weak pointers)
|
||||
refCounts_.wait(prevIntVal - kWeakDelta, std::memory_order_acquire);
|
||||
refCounts_.wait(curIntVal, std::memory_order_acquire);
|
||||
}
|
||||
return ReleaseWeakRefAction::Destroy;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
using KeyCache = TaggedCache<uint256, int, true>;
|
||||
using KeyCache = TaggedCache<UInt256, int, true>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -350,10 +350,10 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
|
||||
class Number final
|
||||
{
|
||||
using rep = std::int64_t;
|
||||
using internalrep = MantissaRange::rep;
|
||||
using InternalRep = MantissaRange::rep;
|
||||
|
||||
bool negative_{false};
|
||||
internalrep mantissa_{0};
|
||||
InternalRep mantissa_{0};
|
||||
int exponent_{std::numeric_limits<int>::lowest()};
|
||||
|
||||
public:
|
||||
@@ -361,10 +361,10 @@ public:
|
||||
static constexpr int kMinExponent = -32768;
|
||||
static constexpr int kMaxExponent = 32768;
|
||||
|
||||
static constexpr internalrep kMaxRep = std::numeric_limits<rep>::max();
|
||||
static constexpr InternalRep kMaxRep = std::numeric_limits<rep>::max();
|
||||
static_assert(kMaxRep == 9'223'372'036'854'775'807);
|
||||
static_assert(-kMaxRep == std::numeric_limits<rep>::min() + 1);
|
||||
static constexpr internalrep kMaxRepUp = ((kMaxRep / 10) + 1) * 10;
|
||||
static constexpr InternalRep kMaxRepUp = ((kMaxRep / 10) + 1) * 10;
|
||||
static_assert(kMaxRepUp == 9'223'372'036'854'775'810ULL);
|
||||
|
||||
// May need to make unchecked private
|
||||
@@ -388,15 +388,15 @@ public:
|
||||
explicit Number(rep mantissa, int exponent);
|
||||
explicit constexpr Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
InternalRep mantissa,
|
||||
int exponent,
|
||||
Unchecked) noexcept;
|
||||
// Assume unsigned values are... unsigned. i.e. positive
|
||||
explicit constexpr Number(internalrep mantissa, int exponent, Unchecked) noexcept;
|
||||
explicit constexpr Number(InternalRep mantissa, int exponent, Unchecked) noexcept;
|
||||
// Only unit tests are expected to use this ctor
|
||||
explicit Number(bool negative, internalrep mantissa, int exponent, Normalized);
|
||||
explicit Number(bool negative, InternalRep mantissa, int exponent, Normalized);
|
||||
// Assume unsigned values are... unsigned. i.e. positive
|
||||
explicit Number(internalrep mantissa, int exponent, Normalized);
|
||||
explicit Number(InternalRep mantissa, int exponent, Normalized);
|
||||
|
||||
[[nodiscard]] constexpr rep
|
||||
mantissa() const noexcept;
|
||||
@@ -558,13 +558,13 @@ public:
|
||||
static void
|
||||
setMantissaScale(MantissaRange::MantissaScale scale);
|
||||
|
||||
static internalrep
|
||||
static InternalRep
|
||||
minMantissa()
|
||||
{
|
||||
return kRange.get().min;
|
||||
}
|
||||
|
||||
static internalrep
|
||||
static InternalRep
|
||||
maxMantissa()
|
||||
{
|
||||
return kRange.get().max;
|
||||
@@ -591,7 +591,7 @@ public:
|
||||
// is negative, returns the positive value. This takes a little extra work
|
||||
// because converting std::numeric_limits<std::int64_t>::min() flirts with
|
||||
// UB, and can vary across compilers.
|
||||
static internalrep
|
||||
static InternalRep
|
||||
externalToInternal(rep mantissa);
|
||||
|
||||
private:
|
||||
@@ -625,8 +625,8 @@ private:
|
||||
bool& negative,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
InternalRep const& minMantissa,
|
||||
InternalRep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix);
|
||||
|
||||
template <class T>
|
||||
@@ -650,25 +650,25 @@ private:
|
||||
shiftExponent(int exponentDelta) const;
|
||||
};
|
||||
|
||||
constexpr Number::Number(bool negative, internalrep mantissa, int exponent, Unchecked) noexcept
|
||||
constexpr Number::Number(bool negative, InternalRep mantissa, int exponent, Unchecked) noexcept
|
||||
: negative_(negative), mantissa_{mantissa}, exponent_{exponent}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Number::Number(internalrep mantissa, int exponent, Unchecked) noexcept
|
||||
constexpr Number::Number(InternalRep mantissa, int exponent, Unchecked) noexcept
|
||||
: Number(false, mantissa, exponent, Unchecked{})
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr Number kNumZero{};
|
||||
|
||||
inline Number::Number(bool negative, internalrep mantissa, int exponent, Normalized)
|
||||
inline Number::Number(bool negative, InternalRep mantissa, int exponent, Normalized)
|
||||
: Number(negative, mantissa, exponent, Unchecked{})
|
||||
{
|
||||
normalize(kRange);
|
||||
}
|
||||
|
||||
inline Number::Number(internalrep mantissa, int exponent, Normalized)
|
||||
inline Number::Number(InternalRep mantissa, int exponent, Normalized)
|
||||
: Number(false, mantissa, exponent, Normalized{})
|
||||
{
|
||||
}
|
||||
@@ -853,7 +853,7 @@ Number::normalizeToRange() const
|
||||
static_assert((kMAX + 1) / 10 == kMIN);
|
||||
|
||||
bool negative = negative_;
|
||||
internalrep mantissa = mantissa_;
|
||||
InternalRep mantissa = mantissa_;
|
||||
int exponent = exponent_;
|
||||
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
|
||||
@@ -21,11 +21,11 @@ The module xrpl/basics should contain no dependencies on other modules.
|
||||
- `std::set`
|
||||
- For sorted containers.
|
||||
|
||||
- `xrpl::hash_set`
|
||||
- `xrpl::HashSet`
|
||||
- Where inserts and contains need to be O(1).
|
||||
- For "small" sets, `std::set` might be faster and smaller.
|
||||
|
||||
- `xrpl::hardened_hash_set`
|
||||
- `xrpl::HardenedHashSet`
|
||||
- For data sets where the key could be manipulated by an attacker
|
||||
in an attempt to mount an algorithmic complexity attack: see
|
||||
http://en.wikipedia.org/wiki/Algorithmic_complexity_attack
|
||||
@@ -33,5 +33,5 @@ The module xrpl/basics should contain no dependencies on other modules.
|
||||
The following container is deprecated
|
||||
|
||||
- `std::unordered_set`
|
||||
- Use `xrpl::hash_set` instead, which uses a better hashing algorithm.
|
||||
- Or use `xrpl::hardened_hash_set` to prevent algorithmic complexity attacks.
|
||||
- Use `xrpl::HashSet` instead, which uses a better hashing algorithm.
|
||||
- Or use `xrpl::HardenedHashSet` to prevent algorithmic complexity attacks.
|
||||
@@ -14,20 +14,20 @@ namespace xrpl {
|
||||
|
||||
class SHAMapHash
|
||||
{
|
||||
uint256 hash_;
|
||||
UInt256 hash_;
|
||||
|
||||
public:
|
||||
SHAMapHash() = default;
|
||||
explicit SHAMapHash(uint256 const& hash) : hash_(hash)
|
||||
explicit SHAMapHash(UInt256 const& hash) : hash_(hash)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] uint256 const&
|
||||
[[nodiscard]] UInt256 const&
|
||||
asUInt256() const
|
||||
{
|
||||
return hash_;
|
||||
}
|
||||
uint256&
|
||||
UInt256&
|
||||
asUInt256()
|
||||
{
|
||||
return hash_;
|
||||
|
||||
@@ -59,34 +59,34 @@ template <
|
||||
class T,
|
||||
bool IsKeyCache = false,
|
||||
class SharedWeakUnionPointerType = SharedWeakCachePointer<T>,
|
||||
class SharedPointerType = std::shared_ptr<T>,
|
||||
class SharedPointer = std::shared_ptr<T>,
|
||||
class Hash = HardenedHash<>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Mutex = std::recursive_mutex>
|
||||
class TaggedCache
|
||||
{
|
||||
public:
|
||||
using mutex_type = Mutex;
|
||||
using MutexType = Mutex;
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
using clock_type = beast::AbstractClock<std::chrono::steady_clock>;
|
||||
using shared_weak_combo_pointer_type = SharedWeakUnionPointerType;
|
||||
using shared_pointer_type = SharedPointerType;
|
||||
using ClockType = beast::AbstractClock<std::chrono::steady_clock>;
|
||||
using SharedWeakComboPointerType = SharedWeakUnionPointerType;
|
||||
using SharedPointerType = SharedPointer;
|
||||
|
||||
public:
|
||||
TaggedCache(
|
||||
std::string const& name,
|
||||
int size,
|
||||
clock_type::duration expiration,
|
||||
clock_type& clock,
|
||||
ClockType::duration expiration,
|
||||
ClockType& clock,
|
||||
beast::Journal journal,
|
||||
beast::insight::Collector::ptr const& collector = beast::insight::NullCollector::make());
|
||||
beast::insight::Collector::Ptr const& collector = beast::insight::NullCollector::make());
|
||||
|
||||
public:
|
||||
/**
|
||||
* Return the clock associated with the cache.
|
||||
*/
|
||||
clock_type&
|
||||
ClockType&
|
||||
clock();
|
||||
|
||||
/**
|
||||
@@ -239,7 +239,7 @@ public:
|
||||
bool
|
||||
retrieve(key_type const& key, T& data);
|
||||
|
||||
mutex_type&
|
||||
MutexType&
|
||||
peekMutex();
|
||||
|
||||
std::vector<key_type>
|
||||
@@ -265,7 +265,7 @@ public:
|
||||
|
||||
private:
|
||||
SharedPointerType
|
||||
initialFetch(key_type const& key, std::scoped_lock<mutex_type> const& l);
|
||||
initialFetch(key_type const& key, std::scoped_lock<MutexType> const& l);
|
||||
|
||||
void
|
||||
collectMetrics();
|
||||
@@ -277,7 +277,7 @@ private:
|
||||
Stats(
|
||||
std::string const& prefix,
|
||||
Handler const& handler,
|
||||
beast::insight::Collector::ptr const& collector)
|
||||
beast::insight::Collector::Ptr const& collector)
|
||||
: hook(collector->makeHook(handler))
|
||||
, size(collector->makeGauge(prefix, "size"))
|
||||
, hitRate(collector->makeGauge(prefix, "hit_rate"))
|
||||
@@ -296,14 +296,14 @@ private:
|
||||
class KeyOnlyEntry
|
||||
{
|
||||
public:
|
||||
clock_type::time_point lastAccess;
|
||||
ClockType::time_point lastAccess;
|
||||
|
||||
explicit KeyOnlyEntry(clock_type::time_point const& lastAccess) : lastAccess(lastAccess)
|
||||
explicit KeyOnlyEntry(ClockType::time_point const& lastAccess) : lastAccess(lastAccess)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
touch(clock_type::time_point const& now)
|
||||
touch(ClockType::time_point const& now)
|
||||
{
|
||||
lastAccess = now;
|
||||
}
|
||||
@@ -312,10 +312,10 @@ private:
|
||||
class ValueEntry
|
||||
{
|
||||
public:
|
||||
shared_weak_combo_pointer_type ptr;
|
||||
clock_type::time_point lastAccess;
|
||||
SharedWeakComboPointerType ptr;
|
||||
ClockType::time_point lastAccess;
|
||||
|
||||
ValueEntry(clock_type::time_point const& lastAccess, shared_pointer_type const& ptr)
|
||||
ValueEntry(ClockType::time_point const& lastAccess, SharedPointerType const& ptr)
|
||||
: ptr(ptr), lastAccess(lastAccess)
|
||||
{
|
||||
}
|
||||
@@ -343,7 +343,7 @@ private:
|
||||
return ptr.lock();
|
||||
}
|
||||
void
|
||||
touch(clock_type::time_point const& now)
|
||||
touch(ClockType::time_point const& now)
|
||||
{
|
||||
lastAccess = now;
|
||||
}
|
||||
@@ -351,35 +351,35 @@ private:
|
||||
|
||||
using Entry = std::conditional_t<IsKeyCache, KeyOnlyEntry, ValueEntry>;
|
||||
|
||||
using KeyOnlyCacheType = hardened_partitioned_hash_map<key_type, KeyOnlyEntry, Hash, KeyEqual>;
|
||||
using KeyOnlyCacheType = HardenedPartitionedHashMap<key_type, KeyOnlyEntry, Hash, KeyEqual>;
|
||||
|
||||
using KeyValueCacheType = hardened_partitioned_hash_map<key_type, ValueEntry, Hash, KeyEqual>;
|
||||
using KeyValueCacheType = HardenedPartitionedHashMap<key_type, ValueEntry, Hash, KeyEqual>;
|
||||
|
||||
using cache_type = hardened_partitioned_hash_map<key_type, Entry, Hash, KeyEqual>;
|
||||
using CacheType = HardenedPartitionedHashMap<key_type, Entry, Hash, KeyEqual>;
|
||||
|
||||
[[nodiscard]] std::thread
|
||||
sweepHelper(
|
||||
clock_type::time_point const& whenExpire,
|
||||
[[maybe_unused]] clock_type::time_point const& now,
|
||||
KeyValueCacheType::map_type& partition,
|
||||
ClockType::time_point const& whenExpire,
|
||||
[[maybe_unused]] ClockType::time_point const& now,
|
||||
KeyValueCacheType::MapType& partition,
|
||||
SweptPointersVector& stuffToSweep,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::scoped_lock<std::recursive_mutex> const&);
|
||||
|
||||
[[nodiscard]] std::thread
|
||||
sweepHelper(
|
||||
clock_type::time_point const& whenExpire,
|
||||
clock_type::time_point const& now,
|
||||
KeyOnlyCacheType::map_type& partition,
|
||||
ClockType::time_point const& whenExpire,
|
||||
ClockType::time_point const& now,
|
||||
KeyOnlyCacheType::MapType& partition,
|
||||
SweptPointersVector&,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::scoped_lock<std::recursive_mutex> const&);
|
||||
|
||||
beast::Journal journal_;
|
||||
clock_type& clock_;
|
||||
ClockType& clock_;
|
||||
Stats stats_;
|
||||
|
||||
mutex_type mutable mutex_;
|
||||
MutexType mutable mutex_;
|
||||
|
||||
// Used for logging
|
||||
std::string name_;
|
||||
@@ -388,11 +388,11 @@ private:
|
||||
int const targetSize_;
|
||||
|
||||
// Desired maximum cache age
|
||||
clock_type::duration const targetAge_;
|
||||
ClockType::duration const targetAge_;
|
||||
|
||||
// Number of items cached
|
||||
int cacheCount_{0};
|
||||
cache_type cache_; // Hold strong reference to recent objects
|
||||
CacheType cache_; // Hold strong reference to recent objects
|
||||
std::uint64_t hits_{0};
|
||||
std::uint64_t misses_{0};
|
||||
};
|
||||
|
||||
@@ -54,10 +54,10 @@ inline TaggedCache<
|
||||
TaggedCache(
|
||||
std::string const& name,
|
||||
int size,
|
||||
clock_type::duration expiration,
|
||||
clock_type& clock,
|
||||
ClockType::duration expiration,
|
||||
ClockType& clock,
|
||||
beast::Journal journal,
|
||||
beast::insight::Collector::ptr const& collector)
|
||||
beast::insight::Collector::Ptr const& collector)
|
||||
: journal_(journal)
|
||||
, clock_(clock)
|
||||
, stats_(
|
||||
@@ -81,7 +81,7 @@ template <
|
||||
class Mutex>
|
||||
inline auto
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
clock() -> clock_type&
|
||||
clock() -> ClockType&
|
||||
{
|
||||
return clock_;
|
||||
}
|
||||
@@ -237,8 +237,8 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
// is destroyed but still within the main cache lock.
|
||||
std::vector<SweptPointersVector> allStuffToSweep(cache_.partitions());
|
||||
|
||||
clock_type::time_point const now(clock_.now());
|
||||
clock_type::time_point whenExpire;
|
||||
ClockType::time_point const now(clock_.now());
|
||||
ClockType::time_point whenExpire;
|
||||
|
||||
auto const start = std::chrono::steady_clock::now();
|
||||
{
|
||||
@@ -252,7 +252,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
{
|
||||
whenExpire = now - (targetAge_ * targetSize_ / cache_.size());
|
||||
|
||||
clock_type::duration const minimumAge(std::chrono::seconds(1));
|
||||
ClockType::duration const minimumAge(std::chrono::seconds(1));
|
||||
if (whenExpire > (now - minimumAge))
|
||||
whenExpire = now - minimumAge;
|
||||
|
||||
@@ -487,7 +487,7 @@ inline SharedPointerType
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
fetch(key_type const& key)
|
||||
{
|
||||
std::scoped_lock<mutex_type> const l(mutex_);
|
||||
std::scoped_lock<MutexType> const l(mutex_);
|
||||
auto ret = initialFetch(key, l);
|
||||
if (!ret)
|
||||
++misses_;
|
||||
@@ -541,7 +541,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
requires IsKeyCache
|
||||
{
|
||||
std::scoped_lock const lock(mutex_);
|
||||
clock_type::time_point const now(clock_.now());
|
||||
ClockType::time_point const now(clock_.now());
|
||||
auto [it, inserted] = cache_.emplace(
|
||||
std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(now));
|
||||
if (!inserted)
|
||||
@@ -583,7 +583,7 @@ template <
|
||||
class Mutex>
|
||||
inline auto
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
peekMutex() -> mutex_type&
|
||||
peekMutex() -> MutexType&
|
||||
{
|
||||
return mutex_;
|
||||
}
|
||||
@@ -711,7 +711,7 @@ template <
|
||||
class Mutex>
|
||||
inline SharedPointerType
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
initialFetch(key_type const& key, std::scoped_lock<mutex_type> const& l)
|
||||
initialFetch(key_type const& key, std::scoped_lock<MutexType> const& l)
|
||||
{
|
||||
auto cit = cache_.find(key);
|
||||
if (cit == cache_.end())
|
||||
@@ -776,9 +776,9 @@ template <
|
||||
inline std::thread
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
sweepHelper(
|
||||
clock_type::time_point const& whenExpire,
|
||||
[[maybe_unused]] clock_type::time_point const& now,
|
||||
KeyValueCacheType::map_type& partition,
|
||||
ClockType::time_point const& whenExpire,
|
||||
[[maybe_unused]] ClockType::time_point const& now,
|
||||
KeyValueCacheType::MapType& partition,
|
||||
SweptPointersVector& stuffToSweep,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::scoped_lock<std::recursive_mutex> const&)
|
||||
@@ -856,9 +856,9 @@ template <
|
||||
inline std::thread
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
sweepHelper(
|
||||
clock_type::time_point const& whenExpire,
|
||||
clock_type::time_point const& now,
|
||||
KeyOnlyCacheType::map_type& partition,
|
||||
ClockType::time_point const& whenExpire,
|
||||
ClockType::time_point const& now,
|
||||
KeyOnlyCacheType::MapType& partition,
|
||||
SweptPointersVector&,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::scoped_lock<std::recursive_mutex> const&)
|
||||
|
||||
@@ -33,7 +33,7 @@ template <
|
||||
class Hash = beast::Uhash<>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, Value>>>
|
||||
using hash_map = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
|
||||
using HashMap = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Key,
|
||||
@@ -41,62 +41,62 @@ template <
|
||||
class Hash = beast::Uhash<>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, Value>>>
|
||||
using hash_multimap = std::unordered_multimap<Key, Value, Hash, Pred, Allocator>;
|
||||
using HashMultimap = std::unordered_multimap<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Value,
|
||||
class Hash = beast::Uhash<>,
|
||||
class Pred = std::equal_to<Value>,
|
||||
class Allocator = std::allocator<Value>>
|
||||
using hash_set = std::unordered_set<Value, Hash, Pred, Allocator>;
|
||||
using HashSet = std::unordered_set<Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Value,
|
||||
class Hash = beast::Uhash<>,
|
||||
class Pred = std::equal_to<Value>,
|
||||
class Allocator = std::allocator<Value>>
|
||||
using hash_multiset = std::unordered_multiset<Value, Hash, Pred, Allocator>;
|
||||
using HashMultiset = std::unordered_multiset<Value, Hash, Pred, Allocator>;
|
||||
|
||||
// hardened_hash containers
|
||||
|
||||
using strong_hash = beast::Xxhasher;
|
||||
using StrongHash = beast::Xxhasher;
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class Value,
|
||||
class Hash = HardenedHash<strong_hash>,
|
||||
class Hash = HardenedHash<StrongHash>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, Value>>>
|
||||
using hardened_hash_map = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
|
||||
using HardenedHashMap = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class Value,
|
||||
class Hash = HardenedHash<strong_hash>,
|
||||
class Hash = HardenedHash<StrongHash>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, Value>>>
|
||||
using hardened_partitioned_hash_map = PartitionedUnorderedMap<Key, Value, Hash, Pred, Allocator>;
|
||||
using HardenedPartitionedHashMap = PartitionedUnorderedMap<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class Value,
|
||||
class Hash = HardenedHash<strong_hash>,
|
||||
class Hash = HardenedHash<StrongHash>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, Value>>>
|
||||
using hardened_hash_multimap = std::unordered_multimap<Key, Value, Hash, Pred, Allocator>;
|
||||
using HardenedHashMultimap = std::unordered_multimap<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Value,
|
||||
class Hash = HardenedHash<strong_hash>,
|
||||
class Hash = HardenedHash<StrongHash>,
|
||||
class Pred = std::equal_to<Value>,
|
||||
class Allocator = std::allocator<Value>>
|
||||
using hardened_hash_set = std::unordered_set<Value, Hash, Pred, Allocator>;
|
||||
using HardenedHashSet = std::unordered_set<Value, Hash, Pred, Allocator>;
|
||||
|
||||
template <
|
||||
class Value,
|
||||
class Hash = HardenedHash<strong_hash>,
|
||||
class Hash = HardenedHash<StrongHash>,
|
||||
class Pred = std::equal_to<Value>,
|
||||
class Allocator = std::allocator<Value>>
|
||||
using hardened_hash_multiset = std::unordered_multiset<Value, Hash, Pred, Allocator>;
|
||||
using HardenedHashMultiset = std::unordered_multiset<Value, Hash, Pred, Allocator>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using tag_type = Tag;
|
||||
using TagType = Tag;
|
||||
|
||||
pointer
|
||||
data()
|
||||
@@ -575,10 +575,16 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using uint128 = BaseUInt<128>;
|
||||
using uint160 = BaseUInt<160>;
|
||||
using uint256 = BaseUInt<256>;
|
||||
using uint192 = BaseUInt<192>;
|
||||
using UInt128 = BaseUInt<128>;
|
||||
using UInt160 = BaseUInt<160>;
|
||||
using UInt256 = BaseUInt<256>;
|
||||
using UInt192 = BaseUInt<192>;
|
||||
|
||||
// TODO [#8340]: Legacy names still used by external consumers of libxrpl (e.g. rpc-spec).
|
||||
// Do not use in new code; remove once those consumers have migrated.
|
||||
using uint160 = UInt160; // NOLINT(readability-identifier-naming)
|
||||
using uint256 = UInt256; // NOLINT(readability-identifier-naming)
|
||||
using uint192 = UInt192; // NOLINT(readability-identifier-naming)
|
||||
|
||||
template <std::size_t Bits, class Tag>
|
||||
[[nodiscard]] constexpr std::strong_ordering
|
||||
@@ -670,7 +676,7 @@ operator<<(std::ostream& out, BaseUInt<Bits, Tag> const& u)
|
||||
|
||||
template <>
|
||||
inline std::size_t
|
||||
extract(uint256 const& key)
|
||||
extract(UInt256 const& key)
|
||||
{
|
||||
std::size_t result = 0;
|
||||
// Use memcpy to avoid unaligned UB
|
||||
@@ -680,10 +686,10 @@ extract(uint256 const& key)
|
||||
}
|
||||
|
||||
#ifndef __INTELLISENSE__
|
||||
static_assert(sizeof(uint128) == 128 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(uint160) == 160 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(uint192) == 192 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(uint256) == 256 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(UInt128) == 128 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(UInt160) == 160 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(UInt192) == 192 / 8, "There should be no padding bytes");
|
||||
static_assert(sizeof(UInt256) == 256 / 8, "There should be no padding bytes");
|
||||
#endif
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -16,10 +16,10 @@ namespace xrpl {
|
||||
|
||||
// A few handy aliases
|
||||
|
||||
using days =
|
||||
using Days =
|
||||
std::chrono::duration<int, std::ratio_multiply<std::chrono::hours::period, std::ratio<24>>>;
|
||||
|
||||
using weeks = std::chrono::duration<int, std::ratio_multiply<days::period, std::ratio<7>>>;
|
||||
using Weeks = std::chrono::duration<int, std::ratio_multiply<Days::period, std::ratio<7>>>;
|
||||
|
||||
/**
|
||||
* Clock for measuring the network time.
|
||||
|
||||
@@ -11,10 +11,10 @@ namespace xrpl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
using seed_pair = std::pair<std::uint64_t, std::uint64_t>;
|
||||
using SeedPair = std::pair<std::uint64_t, std::uint64_t>;
|
||||
|
||||
template <bool = true>
|
||||
seed_pair
|
||||
SeedPair
|
||||
makeSeedPair() noexcept
|
||||
{
|
||||
struct StateT
|
||||
@@ -71,7 +71,7 @@ template <class HashAlgorithm = beast::Xxhasher>
|
||||
class HardenedHash
|
||||
{
|
||||
private:
|
||||
detail::seed_pair seeds_{detail::makeSeedPair<>()};
|
||||
detail::SeedPair seeds_{detail::makeSeedPair<>()};
|
||||
|
||||
public:
|
||||
using result_type = HashAlgorithm::result_type;
|
||||
|
||||
@@ -53,19 +53,19 @@ public:
|
||||
using const_reference = value_type const&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = value_type const*;
|
||||
using map_type = std::unordered_map<key_type, mapped_type, hasher, key_equal, allocator_type>;
|
||||
using partition_map_type = std::vector<map_type>;
|
||||
using MapType = std::unordered_map<key_type, mapped_type, hasher, key_equal, allocator_type>;
|
||||
using PartitionMapType = std::vector<MapType>;
|
||||
|
||||
struct Iterator
|
||||
{
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
partition_map_type* map{nullptr};
|
||||
partition_map_type::iterator ait{};
|
||||
map_type::iterator mit;
|
||||
PartitionMapType* map{nullptr};
|
||||
PartitionMapType::iterator ait{};
|
||||
MapType::iterator mit;
|
||||
|
||||
Iterator() = default;
|
||||
|
||||
Iterator(partition_map_type* m) : map(m)
|
||||
Iterator(PartitionMapType* m) : map(m)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -122,13 +122,13 @@ public:
|
||||
{
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
partition_map_type* map{nullptr};
|
||||
partition_map_type::iterator ait{};
|
||||
map_type::iterator mit;
|
||||
PartitionMapType* map{nullptr};
|
||||
PartitionMapType::iterator ait{};
|
||||
MapType::iterator mit;
|
||||
|
||||
ConstIterator() = default;
|
||||
|
||||
ConstIterator(partition_map_type* m) : map(m)
|
||||
ConstIterator(PartitionMapType* m) : map(m)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ public:
|
||||
return partitions_;
|
||||
}
|
||||
|
||||
partition_map_type&
|
||||
PartitionMapType&
|
||||
map()
|
||||
{
|
||||
return map_;
|
||||
@@ -377,7 +377,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
mutable partition_map_type map_{};
|
||||
mutable PartitionMapType map_{};
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -14,13 +14,13 @@ namespace xrpl {
|
||||
#ifndef __INTELLISENSE__
|
||||
static_assert(
|
||||
// NOLINTNEXTLINE(misc-redundant-expression)
|
||||
std::is_integral_v<beast::xor_shift_engine::result_type> &&
|
||||
std::is_unsigned_v<beast::xor_shift_engine::result_type>,
|
||||
std::is_integral_v<beast::XorShiftEngine::result_type> &&
|
||||
std::is_unsigned_v<beast::XorShiftEngine::result_type>,
|
||||
"The XRPL default PRNG engine must return an unsigned integral type.");
|
||||
|
||||
static_assert(
|
||||
// NOLINTNEXTLINE(misc-redundant-expression)
|
||||
std::numeric_limits<beast::xor_shift_engine::result_type>::max() >=
|
||||
std::numeric_limits<beast::XorShiftEngine::result_type>::max() >=
|
||||
std::numeric_limits<std::uint64_t>::max(),
|
||||
"The XRPL default PRNG engine return must be at least 64 bits wide.");
|
||||
#endif
|
||||
@@ -30,7 +30,7 @@ namespace detail {
|
||||
// Determines if a type can be called like an Engine
|
||||
// NOLINTNEXTLINE(readability-redundant-typename): typename required by MSVC
|
||||
template <class Engine, class Result = typename Engine::result_type>
|
||||
using is_engine = std::is_invocable_r<Result, Engine>;
|
||||
using IsEngine = std::is_invocable_r<Result, Engine>;
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
@@ -44,28 +44,28 @@ using is_engine = std::is_invocable_r<Result, Engine>;
|
||||
* Each thread gets its own instance of the engine which
|
||||
* will be randomly seeded.
|
||||
*/
|
||||
inline beast::xor_shift_engine&
|
||||
inline beast::XorShiftEngine&
|
||||
defaultPrng()
|
||||
{
|
||||
// This is used to seed the thread-specific PRNGs on demand
|
||||
static beast::xor_shift_engine kSeeder = [] {
|
||||
static beast::XorShiftEngine kSeeder = [] {
|
||||
std::random_device rng;
|
||||
std::uniform_int_distribution<std::uint64_t> distribution{1};
|
||||
return beast::xor_shift_engine(distribution(rng));
|
||||
return beast::XorShiftEngine(distribution(rng));
|
||||
}();
|
||||
|
||||
// This protects the seeder
|
||||
static std::mutex kM;
|
||||
|
||||
// The thread-specific PRNGs:
|
||||
thread_local beast::xor_shift_engine kEngine = [] {
|
||||
thread_local beast::XorShiftEngine kEngine = [] {
|
||||
std::uint64_t seed = 0;
|
||||
{
|
||||
std::scoped_lock const lk(kM);
|
||||
std::uniform_int_distribution<std::uint64_t> distribution{1};
|
||||
seed = distribution(kSeeder);
|
||||
}
|
||||
return beast::xor_shift_engine{seed};
|
||||
return beast::XorShiftEngine{seed};
|
||||
}();
|
||||
|
||||
return kEngine;
|
||||
@@ -95,7 +95,7 @@ defaultPrng()
|
||||
template <class Engine, class Integral>
|
||||
Integral
|
||||
randInt(Engine& engine, Integral min, Integral max)
|
||||
requires(std::is_integral_v<Integral> && detail::is_engine<Engine>::value)
|
||||
requires(std::is_integral_v<Integral> && detail::IsEngine<Engine>::value)
|
||||
{
|
||||
XRPL_ASSERT(max > min, "xrpl::randInt : max over min inputs");
|
||||
|
||||
@@ -116,7 +116,7 @@ randInt(Integral min, Integral max)
|
||||
template <class Engine, class Integral>
|
||||
Integral
|
||||
randInt(Engine& engine, Integral max)
|
||||
requires(std::is_integral_v<Integral> && detail::is_engine<Engine>::value)
|
||||
requires(std::is_integral_v<Integral> && detail::IsEngine<Engine>::value)
|
||||
{
|
||||
return randInt(engine, Integral(0), max);
|
||||
}
|
||||
@@ -132,7 +132,7 @@ randInt(Integral max)
|
||||
template <class Integral, class Engine>
|
||||
Integral
|
||||
randInt(Engine& engine)
|
||||
requires(std::is_integral_v<Integral> && detail::is_engine<Engine>::value)
|
||||
requires(std::is_integral_v<Integral> && detail::IsEngine<Engine>::value)
|
||||
{
|
||||
return randInt(engine, std::numeric_limits<Integral>::max());
|
||||
}
|
||||
@@ -155,7 +155,7 @@ Byte
|
||||
randByte(Engine& engine)
|
||||
requires(
|
||||
(std::is_same_v<Byte, unsigned char> || std::is_same_v<Byte, std::uint8_t>) &&
|
||||
detail::is_engine<Engine>::value)
|
||||
detail::IsEngine<Engine>::value)
|
||||
{
|
||||
return static_cast<Byte>(randInt<Engine, std::uint32_t>(
|
||||
engine, std::numeric_limits<Byte>::min(), std::numeric_limits<Byte>::max()));
|
||||
|
||||
@@ -40,7 +40,7 @@ private:
|
||||
|
||||
public:
|
||||
using value_type = Int;
|
||||
using tag_type = Tag;
|
||||
using TagType = Tag;
|
||||
|
||||
TaggedInteger() = default;
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# beast's containers, clocks and Journal streambuf model std concepts, which mandate
|
||||
# snake_case member type names.
|
||||
#
|
||||
# clang-tidy replaces rather than merges this option, so the list is the
|
||||
# top-level one restated plus the 20 name(s) needed only here:
|
||||
# allocator_type argument_type char_type const_local_iterator const_pointer const_reverse_iterator first_argument_type hasher int_type key_compare key_equal local_iterator native_handle_type off_type pos_type reverse_iterator second_argument_type traits_type type value_compare
|
||||
InheritParentConfig: true
|
||||
CheckOptions:
|
||||
readability-identifier-naming.TypeAliasIgnoredRegexp: "^(allocator_type|argument_type|char_type|const_iterator|const_local_iterator|const_pointer|const_reference|const_reverse_iterator|difference_type|duration|first_argument_type|hasher|int_type|iterator|iterator_category|key_compare|key_equal|key_type|local_iterator|mapped_type|native_handle_type|off_type|period|pointer|pos_type|reference|rep|result_type|reverse_iterator|second_argument_type|size_type|time_point|traits_type|type|value_compare|value_type)$"
|
||||
readability-identifier-naming.TypedefIgnoredRegexp: "^(allocator_type|argument_type|char_type|const_iterator|const_local_iterator|const_pointer|const_reference|const_reverse_iterator|difference_type|duration|first_argument_type|hasher|int_type|iterator|iterator_category|key_compare|key_equal|key_type|local_iterator|mapped_type|native_handle_type|off_type|period|pointer|pos_type|reference|rep|result_type|reverse_iterator|second_argument_type|size_type|time_point|traits_type|type|value_compare|value_type)$"
|
||||
@@ -9,7 +9,7 @@ namespace beast {
|
||||
* an instance of the class can be dependency injected, facilitating
|
||||
* unit tests where time may be controlled.
|
||||
*
|
||||
* An abstract_clock inherits all the nested types of the Clock
|
||||
* An AbstractClock inherits all the nested types of the Clock
|
||||
* template parameter.
|
||||
*
|
||||
* Example:
|
||||
@@ -18,9 +18,9 @@ namespace beast {
|
||||
*
|
||||
* struct Implementation
|
||||
* {
|
||||
* using clock_type = abstract_clock <std::chrono::steady_clock>;
|
||||
* clock_type& clock_;
|
||||
* explicit Implementation (clock_type& clock)
|
||||
* using ClockType = AbstractClock<std::chrono::steady_clock>;
|
||||
* ClockType& clock_;
|
||||
* explicit Implementation(ClockType& clock)
|
||||
* : clock_(clock)
|
||||
* {
|
||||
* }
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
using period = Clock::period;
|
||||
using duration = Clock::duration;
|
||||
using time_point = Clock::time_point;
|
||||
using clock_type = Clock;
|
||||
using ClockType = Clock;
|
||||
|
||||
static bool const is_steady = Clock::is_steady; // NOLINT(readability-identifier-naming)
|
||||
|
||||
|
||||
@@ -15,6 +15,6 @@ template <
|
||||
class Clock = std::chrono::steady_clock,
|
||||
class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T>>>
|
||||
using aged_map = detail::AgedOrderedContainer<false, true, Key, T, Clock, Compare, Allocator>;
|
||||
using AgedMap = detail::AgedOrderedContainer<false, true, Key, T, Clock, Compare, Allocator>;
|
||||
|
||||
} // namespace beast
|
||||
@@ -15,6 +15,6 @@ template <
|
||||
class Clock = std::chrono::steady_clock,
|
||||
class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T>>>
|
||||
using aged_multimap = detail::AgedOrderedContainer<true, true, Key, T, Clock, Compare, Allocator>;
|
||||
using AgedMultimap = detail::AgedOrderedContainer<true, true, Key, T, Clock, Compare, Allocator>;
|
||||
|
||||
} // namespace beast
|
||||
@@ -13,7 +13,7 @@ template <
|
||||
class Clock = std::chrono::steady_clock,
|
||||
class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<Key>>
|
||||
using aged_multiset =
|
||||
using AgedMultiset =
|
||||
detail::AgedOrderedContainer<true, false, Key, void, Clock, Compare, Allocator>;
|
||||
|
||||
} // namespace beast
|
||||
@@ -13,6 +13,6 @@ template <
|
||||
class Clock = std::chrono::steady_clock,
|
||||
class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<Key>>
|
||||
using aged_set = detail::AgedOrderedContainer<false, false, Key, void, Clock, Compare, Allocator>;
|
||||
using AgedSet = detail::AgedOrderedContainer<false, false, Key, void, Clock, Compare, Allocator>;
|
||||
|
||||
} // namespace beast
|
||||
@@ -16,7 +16,7 @@ template <
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T>>>
|
||||
using aged_unordered_map =
|
||||
using AgedUnorderedMap =
|
||||
detail::AgedUnorderedContainer<false, true, Key, T, Clock, Hash, KeyEqual, Allocator>;
|
||||
|
||||
} // namespace beast
|
||||
@@ -16,7 +16,7 @@ template <
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T>>>
|
||||
using aged_unordered_multimap =
|
||||
using AgedUnorderedMultimap =
|
||||
detail::AgedUnorderedContainer<true, true, Key, T, Clock, Hash, KeyEqual, Allocator>;
|
||||
|
||||
} // namespace beast
|
||||
@@ -14,7 +14,7 @@ template <
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>>
|
||||
using aged_unordered_multiset =
|
||||
using AgedUnorderedMultiset =
|
||||
detail::AgedUnorderedContainer<true, false, Key, void, Clock, Hash, KeyEqual, Allocator>;
|
||||
|
||||
} // namespace beast
|
||||
@@ -14,7 +14,7 @@ template <
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>>
|
||||
using aged_unordered_set =
|
||||
using AgedUnorderedSet =
|
||||
detail::AgedUnorderedContainer<false, false, Key, void, Clock, Hash, KeyEqual, Allocator>;
|
||||
|
||||
} // namespace beast
|
||||
@@ -4,12 +4,7 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
template <bool, bool, class, class, class, class, class>
|
||||
class aged_ordered_container;
|
||||
|
||||
namespace detail {
|
||||
namespace beast::detail {
|
||||
|
||||
// If Iterator is SCARY then this iterator will be as well.
|
||||
template <bool IsConst, class Iterator>
|
||||
@@ -144,6 +139,4 @@ private:
|
||||
Iterator iter_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace beast
|
||||
} // namespace beast::detail
|
||||
@@ -54,7 +54,7 @@ struct IsBoostReverseIterator<boost::intrusive::reverse_iterator<It>> : std::tru
|
||||
* whose items expire after a certain amount of time. The chronological
|
||||
* iterators allow for fully customizable expiration strategies.
|
||||
*
|
||||
* @see aged_set, aged_multiset, aged_map, aged_multimap
|
||||
* @see AgedSet, AgedMultiset, AgedMap, AgedMultimap
|
||||
*/
|
||||
template <
|
||||
bool IsMulti,
|
||||
@@ -67,9 +67,9 @@ template <
|
||||
class AgedOrderedContainer
|
||||
{
|
||||
public:
|
||||
using clock_type = AbstractClock<Clock>;
|
||||
using time_point = clock_type::time_point;
|
||||
using duration = clock_type::duration;
|
||||
using ClockType = AbstractClock<Clock>;
|
||||
using time_point = ClockType::time_point;
|
||||
using duration = ClockType::duration;
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
using value_type = std::conditional_t<IsMap, std::pair<Key const, T>, Key>;
|
||||
@@ -77,9 +77,9 @@ public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// Introspection (for unit tests)
|
||||
using is_unordered = std::false_type;
|
||||
using is_multi = std::integral_constant<bool, IsMulti>;
|
||||
using is_map = std::integral_constant<bool, IsMap>;
|
||||
using IsUnorderedType = std::false_type;
|
||||
using IsMultiType = std::integral_constant<bool, IsMulti>;
|
||||
using IsMapType = std::integral_constant<bool, IsMap>;
|
||||
|
||||
private:
|
||||
static Key const&
|
||||
@@ -127,8 +127,8 @@ private:
|
||||
class PairValueCompare : public Compare
|
||||
{
|
||||
public:
|
||||
using first_argument = value_type;
|
||||
using second_argument = value_type;
|
||||
using FirstArgument = value_type;
|
||||
using SecondArgument = value_type;
|
||||
using result_type = bool;
|
||||
|
||||
bool
|
||||
@@ -156,8 +156,8 @@ private:
|
||||
class KeyValueCompare : public Compare
|
||||
{
|
||||
public:
|
||||
using first_argument = Key;
|
||||
using second_argument = Element;
|
||||
using FirstArgument = Key;
|
||||
using SecondArgument = Element;
|
||||
using result_type = bool;
|
||||
|
||||
KeyValueCompare() = default;
|
||||
@@ -197,10 +197,10 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
using list_type =
|
||||
using ListType =
|
||||
boost::intrusive::make_list<Element, boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using cont_type = std::conditional_t<
|
||||
using ContType = std::conditional_t<
|
||||
IsMulti,
|
||||
typename boost::intrusive::make_multiset<
|
||||
Element,
|
||||
@@ -219,20 +219,20 @@ private:
|
||||
public beast::detail::EmptyBaseOptimization<ElementAllocator>
|
||||
{
|
||||
public:
|
||||
explicit ConfigT(clock_type& clock) : clock(clock)
|
||||
explicit ConfigT(ClockType& clock) : clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigT(clock_type& clock, Compare const& comp) : KeyValueCompare(comp), clock(clock)
|
||||
ConfigT(ClockType& clock, Compare const& comp) : KeyValueCompare(comp), clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigT(clock_type& clock, Allocator const& alloc)
|
||||
ConfigT(ClockType& clock, Allocator const& alloc)
|
||||
: beast::detail::EmptyBaseOptimization<ElementAllocator>(alloc), clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigT(clock_type& clock, Compare const& comp, Allocator const& alloc)
|
||||
ConfigT(ClockType& clock, Compare const& comp, Allocator const& alloc)
|
||||
: KeyValueCompare(comp)
|
||||
, beast::detail::EmptyBaseOptimization<ElementAllocator>(alloc)
|
||||
, clock(clock)
|
||||
@@ -328,7 +328,7 @@ private:
|
||||
return beast::detail::EmptyBaseOptimization<ElementAllocator>::member();
|
||||
}
|
||||
|
||||
std::reference_wrapper<clock_type> clock;
|
||||
std::reference_wrapper<ClockType> clock;
|
||||
};
|
||||
|
||||
template <class... Args>
|
||||
@@ -383,12 +383,12 @@ public:
|
||||
|
||||
// A set iterator (IsMap==false) is always const
|
||||
// because the elements of a set are immutable.
|
||||
using iterator = beast::detail::AgedContainerIterator<!IsMap, typename cont_type::iterator>;
|
||||
using const_iterator = beast::detail::AgedContainerIterator<true, typename cont_type::iterator>;
|
||||
using iterator = beast::detail::AgedContainerIterator<!IsMap, typename ContType::iterator>;
|
||||
using const_iterator = beast::detail::AgedContainerIterator<true, typename ContType::iterator>;
|
||||
using reverse_iterator =
|
||||
beast::detail::AgedContainerIterator<!IsMap, typename cont_type::reverse_iterator>;
|
||||
beast::detail::AgedContainerIterator<!IsMap, typename ContType::reverse_iterator>;
|
||||
using const_reverse_iterator =
|
||||
beast::detail::AgedContainerIterator<true, typename cont_type::reverse_iterator>;
|
||||
beast::detail::AgedContainerIterator<true, typename ContType::reverse_iterator>;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
@@ -406,13 +406,13 @@ public:
|
||||
public:
|
||||
// A set iterator (IsMap==false) is always const
|
||||
// because the elements of a set are immutable.
|
||||
using iterator = beast::detail::AgedContainerIterator<!IsMap, typename list_type::iterator>;
|
||||
using iterator = beast::detail::AgedContainerIterator<!IsMap, typename ListType::iterator>;
|
||||
using const_iterator =
|
||||
beast::detail::AgedContainerIterator<true, typename list_type::iterator>;
|
||||
beast::detail::AgedContainerIterator<true, typename ListType::iterator>;
|
||||
using reverse_iterator =
|
||||
beast::detail::AgedContainerIterator<!IsMap, typename list_type::reverse_iterator>;
|
||||
beast::detail::AgedContainerIterator<!IsMap, typename ListType::reverse_iterator>;
|
||||
using const_reverse_iterator =
|
||||
beast::detail::AgedContainerIterator<true, typename list_type::reverse_iterator>;
|
||||
beast::detail::AgedContainerIterator<true, typename ListType::reverse_iterator>;
|
||||
|
||||
iterator
|
||||
begin()
|
||||
@@ -509,7 +509,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class AgedOrderedContainer;
|
||||
list_type mutable list_;
|
||||
ListType mutable list_;
|
||||
} chronological;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -520,28 +520,28 @@ public:
|
||||
|
||||
AgedOrderedContainer() = delete;
|
||||
|
||||
explicit AgedOrderedContainer(clock_type& clock);
|
||||
explicit AgedOrderedContainer(ClockType& clock);
|
||||
|
||||
AgedOrderedContainer(clock_type& clock, Compare const& comp);
|
||||
AgedOrderedContainer(ClockType& clock, Compare const& comp);
|
||||
|
||||
AgedOrderedContainer(clock_type& clock, Allocator const& alloc);
|
||||
AgedOrderedContainer(ClockType& clock, Allocator const& alloc);
|
||||
|
||||
AgedOrderedContainer(clock_type& clock, Compare const& comp, Allocator const& alloc);
|
||||
AgedOrderedContainer(ClockType& clock, Compare const& comp, Allocator const& alloc);
|
||||
|
||||
template <class InputIt>
|
||||
AgedOrderedContainer(InputIt first, InputIt last, clock_type& clock);
|
||||
AgedOrderedContainer(InputIt first, InputIt last, ClockType& clock);
|
||||
|
||||
template <class InputIt>
|
||||
AgedOrderedContainer(InputIt first, InputIt last, clock_type& clock, Compare const& comp);
|
||||
AgedOrderedContainer(InputIt first, InputIt last, ClockType& clock, Compare const& comp);
|
||||
|
||||
template <class InputIt>
|
||||
AgedOrderedContainer(InputIt first, InputIt last, clock_type& clock, Allocator const& alloc);
|
||||
AgedOrderedContainer(InputIt first, InputIt last, ClockType& clock, Allocator const& alloc);
|
||||
|
||||
template <class InputIt>
|
||||
AgedOrderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Compare const& comp,
|
||||
Allocator const& alloc);
|
||||
|
||||
@@ -556,21 +556,21 @@ public:
|
||||
AgedOrderedContainer&& other,
|
||||
Allocator const& alloc);
|
||||
|
||||
AgedOrderedContainer(std::initializer_list<value_type> init, clock_type& clock);
|
||||
AgedOrderedContainer(std::initializer_list<value_type> init, ClockType& clock);
|
||||
|
||||
AgedOrderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Compare const& comp);
|
||||
|
||||
AgedOrderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Allocator const& alloc);
|
||||
|
||||
AgedOrderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Compare const& comp,
|
||||
Allocator const& alloc);
|
||||
|
||||
@@ -591,13 +591,13 @@ public:
|
||||
return config_.alloc();
|
||||
}
|
||||
|
||||
clock_type&
|
||||
ClockType&
|
||||
clock()
|
||||
{
|
||||
return config_.clock;
|
||||
}
|
||||
|
||||
clock_type const&
|
||||
ClockType const&
|
||||
clock() const
|
||||
{
|
||||
return config_.clock;
|
||||
@@ -820,7 +820,7 @@ public:
|
||||
template <class P, bool MaybeMap = IsMap>
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>
|
||||
insert(P&& value)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P &&>)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P&&>)
|
||||
{
|
||||
return emplace(std::forward<P>(value));
|
||||
}
|
||||
@@ -829,7 +829,7 @@ public:
|
||||
template <class P, bool MaybeMap = IsMap>
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>
|
||||
insert(const_iterator hint, P&& value)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P &&>)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P&&>)
|
||||
{
|
||||
return emplaceHint(hint, std::forward<P>(value));
|
||||
}
|
||||
@@ -1121,7 +1121,7 @@ private:
|
||||
void
|
||||
touch(
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> pos,
|
||||
clock_type::time_point const& now)
|
||||
ClockType::time_point const& now)
|
||||
requires(!IsBoostReverseIterator<Iterator>::value);
|
||||
|
||||
template <
|
||||
@@ -1138,21 +1138,21 @@ private:
|
||||
|
||||
private:
|
||||
ConfigT config_;
|
||||
cont_type mutable cont_;
|
||||
ContType mutable cont_;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
clock_type& clock)
|
||||
ClockType& clock)
|
||||
: config_(clock)
|
||||
{
|
||||
}
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Compare const& comp)
|
||||
: config_(clock, comp), cont_(comp)
|
||||
{
|
||||
@@ -1160,7 +1160,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrd
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, alloc)
|
||||
{
|
||||
@@ -1168,7 +1168,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrd
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Compare const& comp,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, comp, alloc), cont_(comp)
|
||||
@@ -1180,7 +1180,7 @@ template <class InputIt>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock)
|
||||
ClockType& clock)
|
||||
: config_(clock)
|
||||
{
|
||||
insert(first, last);
|
||||
@@ -1191,7 +1191,7 @@ template <class InputIt>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Compare const& comp)
|
||||
: config_(clock, comp), cont_(comp)
|
||||
{
|
||||
@@ -1203,7 +1203,7 @@ template <class InputIt>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, alloc)
|
||||
{
|
||||
@@ -1215,7 +1215,7 @@ template <class InputIt>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Compare const& comp,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, comp, alloc), cont_(comp)
|
||||
@@ -1261,7 +1261,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrd
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock)
|
||||
ClockType& clock)
|
||||
: config_(clock)
|
||||
{
|
||||
insert(init.begin(), init.end());
|
||||
@@ -1270,7 +1270,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrd
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Compare const& comp)
|
||||
: config_(clock, comp), cont_(comp)
|
||||
{
|
||||
@@ -1280,7 +1280,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrd
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, alloc)
|
||||
{
|
||||
@@ -1290,7 +1290,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrd
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::AgedOrderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Compare const& comp,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, comp, alloc), cont_(comp)
|
||||
@@ -1372,7 +1372,7 @@ std::conditional_t<IsMap, T, void*>&
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::operator[](Key const& key)
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(key, std::cref(config_.keyCompare()), d));
|
||||
if (result.second)
|
||||
{
|
||||
@@ -1391,7 +1391,7 @@ std::conditional_t<IsMap, T, void*>&
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::operator[](Key&& key)
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(key, std::cref(config_.keyCompare()), d));
|
||||
if (result.second)
|
||||
{
|
||||
@@ -1426,7 +1426,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::insert(
|
||||
value_type const& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti)
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(extract(value), std::cref(config_.keyCompare()), d));
|
||||
if (result.second)
|
||||
{
|
||||
@@ -1461,7 +1461,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::insert(
|
||||
-> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti && !MaybeMap)
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(extract(value), std::cref(config_.keyCompare()), d));
|
||||
if (result.second)
|
||||
{
|
||||
@@ -1499,7 +1499,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::insert(
|
||||
value_type const& value) -> iterator
|
||||
requires(!MaybeMulti)
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(
|
||||
cont_.insert_check(hint.iterator(), extract(value), std::cref(config_.keyCompare()), d));
|
||||
if (result.second)
|
||||
@@ -1521,7 +1521,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::insert(
|
||||
value_type&& value) -> iterator
|
||||
requires(!MaybeMulti)
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(
|
||||
cont_.insert_check(hint.iterator(), extract(value), std::cref(config_.keyCompare()), d));
|
||||
if (result.second)
|
||||
@@ -1545,7 +1545,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::emplace
|
||||
// VFALCO NOTE Its unfortunate that we need to
|
||||
// construct element here
|
||||
Element* const p(newElement(std::forward<Args>(args)...));
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(extract(p->value), std::cref(config_.keyCompare()), d));
|
||||
if (result.second)
|
||||
{
|
||||
@@ -1584,7 +1584,7 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::emplace
|
||||
// VFALCO NOTE Its unfortunate that we need to
|
||||
// construct element here
|
||||
Element* const p(newElement(std::forward<Args>(args)...));
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(
|
||||
cont_.insert_check(hint.iterator(), extract(p->value), std::cref(config_.keyCompare()), d));
|
||||
if (result.second)
|
||||
@@ -1721,7 +1721,7 @@ template <bool IsConst, class Iterator>
|
||||
void
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::touch(
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> pos,
|
||||
clock_type::time_point const& now)
|
||||
ClockType::time_point const& now)
|
||||
requires(!IsBoostReverseIterator<Iterator>::value)
|
||||
{
|
||||
auto& e(*pos.iterator());
|
||||
|
||||
@@ -59,8 +59,8 @@ namespace detail {
|
||||
* whose items expire after a certain amount of time. The chronological
|
||||
* iterators allow for fully customizable expiration strategies.
|
||||
*
|
||||
* @see aged_unordered_set, aged_unordered_multiset
|
||||
* @see aged_unordered_map, aged_unordered_multimap
|
||||
* @see AgedUnorderedSet, AgedUnorderedMultiset
|
||||
* @see AgedUnorderedMap, AgedUnorderedMultimap
|
||||
*/
|
||||
template <
|
||||
bool IsMulti,
|
||||
@@ -74,9 +74,9 @@ template <
|
||||
class AgedUnorderedContainer
|
||||
{
|
||||
public:
|
||||
using clock_type = AbstractClock<Clock>;
|
||||
using time_point = clock_type::time_point;
|
||||
using duration = clock_type::duration;
|
||||
using ClockType = AbstractClock<Clock>;
|
||||
using time_point = ClockType::time_point;
|
||||
using duration = ClockType::duration;
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
using value_type = std::conditional_t<IsMap, std::pair<Key const, T>, Key>;
|
||||
@@ -84,9 +84,9 @@ public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// Introspection (for unit tests)
|
||||
using is_unordered = std::true_type;
|
||||
using is_multi = std::integral_constant<bool, IsMulti>;
|
||||
using is_map = std::integral_constant<bool, IsMap>;
|
||||
using IsUnorderedType = std::true_type;
|
||||
using IsMultiType = std::integral_constant<bool, IsMulti>;
|
||||
using IsMapType = std::integral_constant<bool, IsMap>;
|
||||
|
||||
private:
|
||||
static Key const&
|
||||
@@ -208,10 +208,10 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
using list_type =
|
||||
using ListType =
|
||||
boost::intrusive::make_list<Element, boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using cont_type = std::conditional_t<
|
||||
using ContType = std::conditional_t<
|
||||
IsMulti,
|
||||
typename boost::intrusive::make_unordered_multiset<
|
||||
Element,
|
||||
@@ -226,8 +226,8 @@ private:
|
||||
boost::intrusive::equal<KeyValueEqual>,
|
||||
boost::intrusive::cache_begin<true>>::type>;
|
||||
|
||||
using bucket_type = cont_type::bucket_type;
|
||||
using bucket_traits = cont_type::bucket_traits;
|
||||
using BucketType = ContType::bucket_type;
|
||||
using BucketTraits = ContType::bucket_traits;
|
||||
|
||||
using ElementAllocator = std::allocator_traits<Allocator>::template rebind_alloc<Element>;
|
||||
|
||||
@@ -242,36 +242,36 @@ private:
|
||||
private beast::detail::EmptyBaseOptimization<ElementAllocator>
|
||||
{
|
||||
public:
|
||||
explicit ConfigT(clock_type& clock) : clock(clock)
|
||||
explicit ConfigT(ClockType& clock) : clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigT(clock_type& clock, Hash const& hash) : ValueHash(hash), clock(clock)
|
||||
ConfigT(ClockType& clock, Hash const& hash) : ValueHash(hash), clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigT(clock_type& clock, KeyEqual const& keyEqual) : KeyValueEqual(keyEqual), clock(clock)
|
||||
ConfigT(ClockType& clock, KeyEqual const& keyEqual) : KeyValueEqual(keyEqual), clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigT(clock_type& clock, Allocator const& alloc)
|
||||
ConfigT(ClockType& clock, Allocator const& alloc)
|
||||
: beast::detail::EmptyBaseOptimization<ElementAllocator>(alloc), clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigT(clock_type& clock, Hash const& hash, KeyEqual const& keyEqual)
|
||||
ConfigT(ClockType& clock, Hash const& hash, KeyEqual const& keyEqual)
|
||||
: ValueHash(hash), KeyValueEqual(keyEqual), clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigT(clock_type& clock, Hash const& hash, Allocator const& alloc)
|
||||
ConfigT(ClockType& clock, Hash const& hash, Allocator const& alloc)
|
||||
: ValueHash(hash)
|
||||
, beast::detail::EmptyBaseOptimization<ElementAllocator>(alloc)
|
||||
, clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigT(clock_type& clock, KeyEqual const& keyEqual, Allocator const& alloc)
|
||||
ConfigT(ClockType& clock, KeyEqual const& keyEqual, Allocator const& alloc)
|
||||
: KeyValueEqual(keyEqual)
|
||||
, beast::detail::EmptyBaseOptimization<ElementAllocator>(alloc)
|
||||
, clock(clock)
|
||||
@@ -279,7 +279,7 @@ private:
|
||||
}
|
||||
|
||||
ConfigT(
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEqual,
|
||||
Allocator const& alloc)
|
||||
@@ -405,29 +405,29 @@ private:
|
||||
return beast::detail::EmptyBaseOptimization<ElementAllocator>::member();
|
||||
}
|
||||
|
||||
std::reference_wrapper<clock_type> clock;
|
||||
std::reference_wrapper<ClockType> clock;
|
||||
};
|
||||
|
||||
class Buckets
|
||||
{
|
||||
public:
|
||||
using vec_type = std::vector<
|
||||
bucket_type,
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<bucket_type>>;
|
||||
using VecType = std::vector<
|
||||
BucketType,
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<BucketType>>;
|
||||
|
||||
Buckets() : maxLoadFactor_(1.f), vec_()
|
||||
{
|
||||
vec_.resize(cont_type::suggested_upper_bucket_count(0));
|
||||
vec_.resize(ContType::suggested_upper_bucket_count(0));
|
||||
}
|
||||
|
||||
Buckets(Allocator const& alloc) : maxLoadFactor_(1.f), vec_(alloc)
|
||||
{
|
||||
vec_.resize(cont_type::suggested_upper_bucket_count(0));
|
||||
vec_.resize(ContType::suggested_upper_bucket_count(0));
|
||||
}
|
||||
|
||||
operator bucket_traits()
|
||||
operator BucketTraits()
|
||||
{
|
||||
return bucket_traits(&vec_[0], vec_.size());
|
||||
return BucketTraits(&vec_[0], vec_.size());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -466,10 +466,10 @@ private:
|
||||
{
|
||||
// Need two vectors otherwise we
|
||||
// will destroy non-empty buckets.
|
||||
vec_type vec(vec_.get_allocator());
|
||||
VecType vec(vec_.get_allocator());
|
||||
std::swap(vec_, vec);
|
||||
vec_.resize(count);
|
||||
c.rehash(bucket_traits(&vec_[0], vec_.size()));
|
||||
c.rehash(BucketTraits(&vec_[0], vec_.size()));
|
||||
return;
|
||||
}
|
||||
// Rehash in place.
|
||||
@@ -478,12 +478,12 @@ private:
|
||||
// This should not reallocate since
|
||||
// we checked capacity earlier.
|
||||
vec_.resize(count);
|
||||
c.rehash(bucket_traits(&vec_[0], count));
|
||||
c.rehash(BucketTraits(&vec_[0], count));
|
||||
return;
|
||||
}
|
||||
// Resize must happen after rehash otherwise
|
||||
// we might destroy non-empty buckets.
|
||||
c.rehash(bucket_traits(&vec_[0], count));
|
||||
c.rehash(BucketTraits(&vec_[0], count));
|
||||
vec_.resize(count);
|
||||
}
|
||||
|
||||
@@ -492,13 +492,13 @@ private:
|
||||
void
|
||||
resize(size_type n, Container& c)
|
||||
{
|
||||
size_type const suggested(cont_type::suggested_upper_bucket_count(n));
|
||||
size_type const suggested(ContType::suggested_upper_bucket_count(n));
|
||||
rehash(suggested, c);
|
||||
}
|
||||
|
||||
private:
|
||||
float maxLoadFactor_;
|
||||
vec_type vec_;
|
||||
VecType vec_;
|
||||
};
|
||||
|
||||
template <class... Args>
|
||||
@@ -553,13 +553,13 @@ public:
|
||||
|
||||
// A set iterator (IsMap==false) is always const
|
||||
// because the elements of a set are immutable.
|
||||
using iterator = beast::detail::AgedContainerIterator<!IsMap, typename cont_type::iterator>;
|
||||
using const_iterator = beast::detail::AgedContainerIterator<true, typename cont_type::iterator>;
|
||||
using iterator = beast::detail::AgedContainerIterator<!IsMap, typename ContType::iterator>;
|
||||
using const_iterator = beast::detail::AgedContainerIterator<true, typename ContType::iterator>;
|
||||
|
||||
using local_iterator =
|
||||
beast::detail::AgedContainerIterator<!IsMap, typename cont_type::local_iterator>;
|
||||
beast::detail::AgedContainerIterator<!IsMap, typename ContType::local_iterator>;
|
||||
using const_local_iterator =
|
||||
beast::detail::AgedContainerIterator<true, typename cont_type::local_iterator>;
|
||||
beast::detail::AgedContainerIterator<true, typename ContType::local_iterator>;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
@@ -575,13 +575,13 @@ public:
|
||||
public:
|
||||
// A set iterator (IsMap==false) is always const
|
||||
// because the elements of a set are immutable.
|
||||
using iterator = beast::detail::AgedContainerIterator<!IsMap, typename list_type::iterator>;
|
||||
using iterator = beast::detail::AgedContainerIterator<!IsMap, typename ListType::iterator>;
|
||||
using const_iterator =
|
||||
beast::detail::AgedContainerIterator<true, typename list_type::iterator>;
|
||||
beast::detail::AgedContainerIterator<true, typename ListType::iterator>;
|
||||
using reverse_iterator =
|
||||
beast::detail::AgedContainerIterator<!IsMap, typename list_type::reverse_iterator>;
|
||||
beast::detail::AgedContainerIterator<!IsMap, typename ListType::reverse_iterator>;
|
||||
using const_reverse_iterator =
|
||||
beast::detail::AgedContainerIterator<true, typename list_type::reverse_iterator>;
|
||||
beast::detail::AgedContainerIterator<true, typename ListType::reverse_iterator>;
|
||||
|
||||
iterator
|
||||
begin()
|
||||
@@ -679,7 +679,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class AgedUnorderedContainer;
|
||||
list_type mutable list_;
|
||||
ListType mutable list_;
|
||||
} chronological;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -690,43 +690,43 @@ public:
|
||||
|
||||
AgedUnorderedContainer() = delete;
|
||||
|
||||
explicit AgedUnorderedContainer(clock_type& clock);
|
||||
explicit AgedUnorderedContainer(ClockType& clock);
|
||||
|
||||
AgedUnorderedContainer(clock_type& clock, Hash const& hash);
|
||||
AgedUnorderedContainer(ClockType& clock, Hash const& hash);
|
||||
|
||||
AgedUnorderedContainer(clock_type& clock, KeyEqual const& keyEq);
|
||||
AgedUnorderedContainer(ClockType& clock, KeyEqual const& keyEq);
|
||||
|
||||
AgedUnorderedContainer(clock_type& clock, Allocator const& alloc);
|
||||
AgedUnorderedContainer(ClockType& clock, Allocator const& alloc);
|
||||
|
||||
AgedUnorderedContainer(clock_type& clock, Hash const& hash, KeyEqual const& keyEq);
|
||||
AgedUnorderedContainer(ClockType& clock, Hash const& hash, KeyEqual const& keyEq);
|
||||
|
||||
AgedUnorderedContainer(clock_type& clock, Hash const& hash, Allocator const& alloc);
|
||||
AgedUnorderedContainer(ClockType& clock, Hash const& hash, Allocator const& alloc);
|
||||
|
||||
AgedUnorderedContainer(clock_type& clock, KeyEqual const& keyEq, Allocator const& alloc);
|
||||
AgedUnorderedContainer(ClockType& clock, KeyEqual const& keyEq, Allocator const& alloc);
|
||||
|
||||
AgedUnorderedContainer(
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEq,
|
||||
Allocator const& alloc);
|
||||
|
||||
template <class InputIt>
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, clock_type& clock);
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, ClockType& clock);
|
||||
|
||||
template <class InputIt>
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, clock_type& clock, Hash const& hash);
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, ClockType& clock, Hash const& hash);
|
||||
|
||||
template <class InputIt>
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, clock_type& clock, KeyEqual const& keyEq);
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, ClockType& clock, KeyEqual const& keyEq);
|
||||
|
||||
template <class InputIt>
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, clock_type& clock, Allocator const& alloc);
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, ClockType& clock, Allocator const& alloc);
|
||||
|
||||
template <class InputIt>
|
||||
AgedUnorderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEq);
|
||||
|
||||
@@ -734,7 +734,7 @@ public:
|
||||
AgedUnorderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
Allocator const& alloc);
|
||||
|
||||
@@ -742,7 +742,7 @@ public:
|
||||
AgedUnorderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
KeyEqual const& keyEq,
|
||||
Allocator const& alloc);
|
||||
|
||||
@@ -750,7 +750,7 @@ public:
|
||||
AgedUnorderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEq,
|
||||
Allocator const& alloc);
|
||||
@@ -766,44 +766,44 @@ public:
|
||||
AgedUnorderedContainer&& other,
|
||||
Allocator const& alloc);
|
||||
|
||||
AgedUnorderedContainer(std::initializer_list<value_type> init, clock_type& clock);
|
||||
AgedUnorderedContainer(std::initializer_list<value_type> init, ClockType& clock);
|
||||
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash);
|
||||
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
KeyEqual const& keyEq);
|
||||
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Allocator const& alloc);
|
||||
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEq);
|
||||
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
Allocator const& alloc);
|
||||
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
KeyEqual const& keyEq,
|
||||
Allocator const& alloc);
|
||||
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEq,
|
||||
Allocator const& alloc);
|
||||
@@ -825,13 +825,13 @@ public:
|
||||
return config_.alloc();
|
||||
}
|
||||
|
||||
clock_type&
|
||||
ClockType&
|
||||
clock()
|
||||
{
|
||||
return config_.clock;
|
||||
}
|
||||
|
||||
clock_type const&
|
||||
ClockType const&
|
||||
clock() const
|
||||
{
|
||||
return config_.clock;
|
||||
@@ -1028,7 +1028,7 @@ public:
|
||||
template <class P, bool MaybeMap = IsMap>
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>
|
||||
insert(P&& value)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P &&>)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P&&>)
|
||||
{
|
||||
return emplace(std::forward<P>(value));
|
||||
}
|
||||
@@ -1037,7 +1037,7 @@ public:
|
||||
template <class P, bool MaybeMap = IsMap>
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>
|
||||
insert(const_iterator hint, P&& value)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P &&>)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P&&>)
|
||||
{
|
||||
return emplaceHint(hint, std::forward<P>(value));
|
||||
}
|
||||
@@ -1399,7 +1399,7 @@ private:
|
||||
void
|
||||
touch(
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> pos,
|
||||
clock_type::time_point const& now)
|
||||
ClockType::time_point const& now)
|
||||
{
|
||||
auto& e(*pos.iterator());
|
||||
e.when = now;
|
||||
@@ -1433,7 +1433,7 @@ private:
|
||||
private:
|
||||
ConfigT config_;
|
||||
Buckets buck_;
|
||||
cont_type mutable cont_;
|
||||
ContType mutable cont_;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -1448,7 +1448,7 @@ template <
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(clock_type& clock)
|
||||
AgedUnorderedContainer(ClockType& clock)
|
||||
: config_(clock)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
{
|
||||
@@ -1464,7 +1464,7 @@ template <
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(clock_type& clock, Hash const& hash)
|
||||
AgedUnorderedContainer(ClockType& clock, Hash const& hash)
|
||||
: config_(clock, hash)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
{
|
||||
@@ -1480,7 +1480,7 @@ template <
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(clock_type& clock, KeyEqual const& keyEq)
|
||||
AgedUnorderedContainer(ClockType& clock, KeyEqual const& keyEq)
|
||||
: config_(clock, keyEq)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
{
|
||||
@@ -1496,7 +1496,7 @@ template <
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(clock_type& clock, Allocator const& alloc)
|
||||
AgedUnorderedContainer(ClockType& clock, Allocator const& alloc)
|
||||
: config_(clock, alloc)
|
||||
, buck_(alloc)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
@@ -1513,7 +1513,7 @@ template <
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(clock_type& clock, Hash const& hash, KeyEqual const& keyEq)
|
||||
AgedUnorderedContainer(ClockType& clock, Hash const& hash, KeyEqual const& keyEq)
|
||||
: config_(clock, hash, keyEq)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
{
|
||||
@@ -1529,7 +1529,7 @@ template <
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(clock_type& clock, Hash const& hash, Allocator const& alloc)
|
||||
AgedUnorderedContainer(ClockType& clock, Hash const& hash, Allocator const& alloc)
|
||||
: config_(clock, hash, alloc)
|
||||
, buck_(alloc)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
@@ -1546,7 +1546,7 @@ template <
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(clock_type& clock, KeyEqual const& keyEq, Allocator const& alloc)
|
||||
AgedUnorderedContainer(ClockType& clock, KeyEqual const& keyEq, Allocator const& alloc)
|
||||
: config_(clock, keyEq, alloc)
|
||||
, buck_(alloc)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
@@ -1564,7 +1564,7 @@ template <
|
||||
class Allocator>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEq,
|
||||
Allocator const& alloc)
|
||||
@@ -1585,7 +1585,7 @@ template <
|
||||
class Allocator>
|
||||
template <class InputIt>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, clock_type& clock)
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, ClockType& clock)
|
||||
: config_(clock)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
{
|
||||
@@ -1603,7 +1603,7 @@ template <
|
||||
class Allocator>
|
||||
template <class InputIt>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, clock_type& clock, Hash const& hash)
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, ClockType& clock, Hash const& hash)
|
||||
: config_(clock, hash)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
{
|
||||
@@ -1621,7 +1621,7 @@ template <
|
||||
class Allocator>
|
||||
template <class InputIt>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, clock_type& clock, KeyEqual const& keyEq)
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, ClockType& clock, KeyEqual const& keyEq)
|
||||
: config_(clock, keyEq)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
{
|
||||
@@ -1639,7 +1639,7 @@ template <
|
||||
class Allocator>
|
||||
template <class InputIt>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, clock_type& clock, Allocator const& alloc)
|
||||
AgedUnorderedContainer(InputIt first, InputIt last, ClockType& clock, Allocator const& alloc)
|
||||
: config_(clock, alloc)
|
||||
, buck_(alloc)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
@@ -1661,7 +1661,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
AgedUnorderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEq)
|
||||
: config_(clock, hash, keyEq)
|
||||
@@ -1684,7 +1684,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
AgedUnorderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, hash, alloc)
|
||||
@@ -1708,7 +1708,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
AgedUnorderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
KeyEqual const& keyEq,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, keyEq, alloc)
|
||||
@@ -1732,7 +1732,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
AgedUnorderedContainer(
|
||||
InputIt first,
|
||||
InputIt last,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEq,
|
||||
Allocator const& alloc)
|
||||
@@ -1829,7 +1829,7 @@ template <
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(std::initializer_list<value_type> init, clock_type& clock)
|
||||
AgedUnorderedContainer(std::initializer_list<value_type> init, ClockType& clock)
|
||||
: config_(clock)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
{
|
||||
@@ -1848,7 +1848,7 @@ template <
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash)
|
||||
: config_(clock, hash)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
@@ -1868,7 +1868,7 @@ template <
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
KeyEqual const& keyEq)
|
||||
: config_(clock, keyEq)
|
||||
, cont_(buck_, std::cref(config_.valueHash()), std::cref(config_.keyValueEqual()))
|
||||
@@ -1888,7 +1888,7 @@ template <
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, alloc)
|
||||
, buck_(alloc)
|
||||
@@ -1909,7 +1909,7 @@ template <
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEq)
|
||||
: config_(clock, hash, keyEq)
|
||||
@@ -1930,7 +1930,7 @@ template <
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, hash, alloc)
|
||||
@@ -1952,7 +1952,7 @@ template <
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
KeyEqual const& keyEq,
|
||||
Allocator const& alloc)
|
||||
: config_(clock, keyEq, alloc)
|
||||
@@ -1974,7 +1974,7 @@ template <
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::
|
||||
AgedUnorderedContainer(
|
||||
std::initializer_list<value_type> init,
|
||||
clock_type& clock,
|
||||
ClockType& clock,
|
||||
Hash const& hash,
|
||||
KeyEqual const& keyEq,
|
||||
Allocator const& alloc)
|
||||
@@ -2127,7 +2127,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
maybeRehash(1);
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(
|
||||
key, std::cref(config_.hashFunction()), std::cref(config_.keyValueEqual()), d));
|
||||
if (result.second)
|
||||
@@ -2157,7 +2157,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
maybeRehash(1);
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(
|
||||
key, std::cref(config_.hashFunction()), std::cref(config_.keyValueEqual()), d));
|
||||
if (result.second)
|
||||
@@ -2211,7 +2211,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
requires(!MaybeMulti)
|
||||
{
|
||||
maybeRehash(1);
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(
|
||||
extract(value), std::cref(config_.hashFunction()), std::cref(config_.keyValueEqual()), d));
|
||||
if (result.second)
|
||||
@@ -2264,7 +2264,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
requires(!MaybeMulti && !MaybeMap)
|
||||
{
|
||||
maybeRehash(1);
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(
|
||||
extract(value), std::cref(config_.hashFunction()), std::cref(config_.keyValueEqual()), d));
|
||||
if (result.second)
|
||||
@@ -2374,7 +2374,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
// VFALCO NOTE Its unfortunate that we need to
|
||||
// construct element here
|
||||
Element* const p(newElement(std::forward<Args>(args)...));
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(
|
||||
extract(p->value),
|
||||
std::cref(config_.hashFunction()),
|
||||
@@ -2611,7 +2611,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
value_type const& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti)
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
typename ContType::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(
|
||||
extract(value), std::cref(config_.hashFunction()), std::cref(config_.keyValueEqual()), d));
|
||||
if (result.second)
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace beast::insight {
|
||||
class Collector
|
||||
{
|
||||
public:
|
||||
using ptr = std::shared_ptr<Collector>;
|
||||
using Ptr = std::shared_ptr<Collector>;
|
||||
|
||||
virtual ~Collector() = 0;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace beast::insight {
|
||||
class Group : public Collector
|
||||
{
|
||||
public:
|
||||
using ptr = std::shared_ptr<Group>;
|
||||
using Ptr = std::shared_ptr<Group>;
|
||||
|
||||
/**
|
||||
* Returns the name of this group, for diagnostics.
|
||||
|
||||
@@ -20,10 +20,10 @@ public:
|
||||
* Find or create a new collector with a given name.
|
||||
*/
|
||||
/** @{ */
|
||||
virtual Group::ptr const&
|
||||
virtual Group::Ptr const&
|
||||
get(std::string const& name) = 0;
|
||||
|
||||
Group::ptr const&
|
||||
Group::Ptr const&
|
||||
operator[](std::string const& name)
|
||||
{
|
||||
return get(name);
|
||||
@@ -35,6 +35,6 @@ public:
|
||||
* Create a group container that uses the specified collector.
|
||||
*/
|
||||
std::unique_ptr<Groups>
|
||||
makeGroups(Collector::ptr const& collector);
|
||||
makeGroups(Collector::Ptr const& collector);
|
||||
|
||||
} // namespace beast::insight
|
||||
@@ -108,12 +108,12 @@ Result
|
||||
split(FwdIt first, FwdIt last, Char delim)
|
||||
{
|
||||
using namespace detail;
|
||||
using string = Result::value_type;
|
||||
using String = Result::value_type;
|
||||
|
||||
Result result;
|
||||
|
||||
FwdIt iter = first;
|
||||
string e;
|
||||
String e;
|
||||
while (iter != last)
|
||||
{
|
||||
if (*iter == '"')
|
||||
@@ -205,10 +205,10 @@ splitCommas(std::string_view s)
|
||||
*/
|
||||
class ListIterator
|
||||
{
|
||||
using iter_type = boost::string_ref::const_iterator;
|
||||
using IterType = boost::string_ref::const_iterator;
|
||||
|
||||
iter_type it_;
|
||||
iter_type end_;
|
||||
IterType it_;
|
||||
IterType end_;
|
||||
boost::string_ref value_;
|
||||
|
||||
public:
|
||||
@@ -218,7 +218,7 @@ public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
ListIterator(iter_type begin, iter_type end) : it_(begin), end_(end)
|
||||
ListIterator(IterType begin, IterType end) : it_(begin), end_(end)
|
||||
{
|
||||
if (it_ != end_)
|
||||
increment();
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
/**
|
||||
* The type of yield context passed to functions.
|
||||
*/
|
||||
using yield_context = boost::asio::yield_context;
|
||||
using YieldContext = boost::asio::yield_context;
|
||||
|
||||
explicit EnableYieldTo(std::size_t concurrency = 1) : work_(boost::asio::make_work_guard(ios_))
|
||||
{
|
||||
@@ -120,7 +120,7 @@ EnableYieldTo::spawn(F0&& f, FN&&... fn)
|
||||
ios_,
|
||||
boost::allocator_arg,
|
||||
boost::context::fixedsize_stack(2 * 1024 * 1024),
|
||||
[&](yield_context yield) {
|
||||
[&](YieldContext yield) {
|
||||
f(yield);
|
||||
std::scoped_lock const lock{m_};
|
||||
if (--running_ == 0)
|
||||
|
||||
@@ -15,29 +15,29 @@ template <class Container>
|
||||
class ConstContainer
|
||||
{
|
||||
private:
|
||||
using cont_type = Container;
|
||||
using ContType = Container;
|
||||
|
||||
cont_type cont_;
|
||||
ContType cont_;
|
||||
|
||||
protected:
|
||||
cont_type&
|
||||
ContType&
|
||||
cont()
|
||||
{
|
||||
return cont_;
|
||||
}
|
||||
|
||||
[[nodiscard]] cont_type const&
|
||||
[[nodiscard]] ContType const&
|
||||
cont() const
|
||||
{
|
||||
return cont_;
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type = cont_type::value_type;
|
||||
using size_type = cont_type::size_type;
|
||||
using difference_type = cont_type::difference_type;
|
||||
using iterator = cont_type::const_iterator;
|
||||
using const_iterator = cont_type::const_iterator;
|
||||
using value_type = ContType::value_type;
|
||||
using size_type = ContType::size_type;
|
||||
using difference_type = ContType::difference_type;
|
||||
using iterator = ContType::const_iterator;
|
||||
using const_iterator = ContType::const_iterator;
|
||||
|
||||
/**
|
||||
* Returns `true` if the container is empty.
|
||||
|
||||
@@ -32,7 +32,7 @@ template <class = void>
|
||||
class Reporter : public Runner
|
||||
{
|
||||
private:
|
||||
using clock_type = std::chrono::steady_clock;
|
||||
using ClockType = std::chrono::steady_clock;
|
||||
|
||||
struct CaseResults
|
||||
{
|
||||
@@ -51,7 +51,7 @@ private:
|
||||
std::size_t cases = 0;
|
||||
std::size_t total = 0;
|
||||
std::size_t failed = 0;
|
||||
clock_type::time_point start = clock_type::now();
|
||||
ClockType::time_point start = ClockType::now();
|
||||
|
||||
explicit SuiteResults(std::string name = "") : name(std::move(name))
|
||||
{
|
||||
@@ -63,7 +63,7 @@ private:
|
||||
|
||||
struct Results
|
||||
{
|
||||
using run_time = std::pair<std::string, clock_type::duration>;
|
||||
using RunTime = std::pair<std::string, ClockType::duration>;
|
||||
|
||||
static constexpr auto kMaxTop = 10;
|
||||
|
||||
@@ -71,8 +71,8 @@ private:
|
||||
std::size_t cases = 0;
|
||||
std::size_t total = 0;
|
||||
std::size_t failed = 0;
|
||||
std::vector<run_time> top;
|
||||
clock_type::time_point start = clock_type::now();
|
||||
std::vector<RunTime> top;
|
||||
ClockType::time_point start = ClockType::now();
|
||||
|
||||
void
|
||||
add(SuiteResults const& r);
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
|
||||
private:
|
||||
static std::string
|
||||
fmtdur(clock_type::duration const& d);
|
||||
fmtdur(ClockType::duration const& d);
|
||||
|
||||
void
|
||||
onSuiteBegin(SuiteInfo const& info) override;
|
||||
@@ -137,14 +137,13 @@ Reporter<Unused>::Results::add(SuiteResults const& r)
|
||||
total += r.total;
|
||||
cases += r.cases;
|
||||
failed += r.failed;
|
||||
auto const elapsed = clock_type::now() - r.start;
|
||||
auto const elapsed = ClockType::now() - r.start;
|
||||
if (elapsed >= std::chrono::seconds{1})
|
||||
{
|
||||
auto const iter = std::lower_bound(
|
||||
top.begin(),
|
||||
top.end(),
|
||||
elapsed,
|
||||
[](run_time const& t1, clock_type::duration const& t2) { return t1.second > t2; });
|
||||
top.begin(), top.end(), elapsed, [](RunTime const& t1, ClockType::duration const& t2) {
|
||||
return t1.second > t2;
|
||||
});
|
||||
if (iter != top.end())
|
||||
{
|
||||
if (top.size() == kMaxTop)
|
||||
@@ -174,7 +173,7 @@ Reporter<Unused>::~Reporter()
|
||||
for (auto const& i : results_.top)
|
||||
os_ << std::setw(8) << fmtdur(i.second) << " " << i.first << '\n';
|
||||
}
|
||||
auto const elapsed = clock_type::now() - results_.start;
|
||||
auto const elapsed = ClockType::now() - results_.start;
|
||||
os_ << fmtdur(elapsed) << ", " << Amount{results_.suites, "suite"} << ", "
|
||||
<< Amount{results_.cases, "case"} << ", " << Amount{results_.total, "test"} << " total, "
|
||||
<< Amount{results_.failed, "failure"} << std::endl;
|
||||
@@ -182,7 +181,7 @@ Reporter<Unused>::~Reporter()
|
||||
|
||||
template <class Unused>
|
||||
std::string
|
||||
Reporter<Unused>::fmtdur(clock_type::duration const& d)
|
||||
Reporter<Unused>::fmtdur(ClockType::duration const& d)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto const ms = duration_cast<milliseconds>(d);
|
||||
@@ -249,6 +248,10 @@ Reporter<Unused>::onLog(std::string const& s)
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using reporter = detail::Reporter<>;
|
||||
using Reporter = detail::Reporter<>;
|
||||
|
||||
// TODO [#8340]: Legacy name still used by external consumers of libxrpl (e.g. validator-keys-tool).
|
||||
// Do not use in new code; remove once those consumers have migrated.
|
||||
using reporter = Reporter; // NOLINT(readability-identifier-naming)
|
||||
|
||||
} // namespace beast::unit_test
|
||||
@@ -643,12 +643,12 @@ Suite::run(Runner& r)
|
||||
* Library Identifies the library.
|
||||
*
|
||||
* The declaration for the class implementing the test should be the same
|
||||
* as Class ## _test. For example, if Class is aged_ordered_container, the
|
||||
* as Class ## _test. For example, if Class is AgedOrderedContainer, the
|
||||
* test class must be declared as:
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* struct aged_ordered_container_test : beast::unit_test::suite
|
||||
* struct AgedOrderedContainer_test : beast::unit_test::suite
|
||||
* {
|
||||
* //...
|
||||
* };
|
||||
|
||||
@@ -18,14 +18,14 @@ class Runner;
|
||||
*/
|
||||
class SuiteInfo
|
||||
{
|
||||
using run_type = std::function<void(Runner&)>;
|
||||
using RunType = std::function<void(Runner&)>;
|
||||
|
||||
std::string name_;
|
||||
std::string module_;
|
||||
std::string library_;
|
||||
bool manual_;
|
||||
int priority_;
|
||||
run_type run_;
|
||||
RunType run_;
|
||||
|
||||
public:
|
||||
SuiteInfo(
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
std::string library,
|
||||
bool manual,
|
||||
int priority,
|
||||
run_type run)
|
||||
RunType run)
|
||||
: name_(std::move(name))
|
||||
, module_(std::move(module))
|
||||
, library_(std::move(library))
|
||||
|
||||
@@ -24,7 +24,7 @@ private:
|
||||
std::thread t_;
|
||||
|
||||
public:
|
||||
using id = std::thread::id;
|
||||
using Id = std::thread::id;
|
||||
using native_handle_type = std::thread::native_handle_type;
|
||||
|
||||
Thread() = default;
|
||||
|
||||
@@ -471,7 +471,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using logstream = BasicLogstream<char>;
|
||||
using logwstream = BasicLogstream<wchar_t>;
|
||||
using LogStream = BasicLogstream<char>;
|
||||
using LogWStream = BasicLogstream<wchar_t>;
|
||||
|
||||
} // namespace beast
|
||||
@@ -19,6 +19,6 @@ struct MaybeConst
|
||||
* Alias for omitting `typename`.
|
||||
*/
|
||||
template <bool IsConst, class T>
|
||||
using maybe_const_t = MaybeConst<IsConst, T>::type;
|
||||
using MaybeConstT = MaybeConst<IsConst, T>::type;
|
||||
|
||||
} // namespace beast
|
||||
@@ -94,6 +94,6 @@ XorShiftEngine<Unused>::murmurhash3(result_type x) -> result_type
|
||||
* http://xorshift.di.unimi.it/xorshift128plus.c
|
||||
* does not accept seed==0
|
||||
*/
|
||||
using xor_shift_engine = detail::XorShiftEngine<>;
|
||||
using XorShiftEngine = detail::XorShiftEngine<>;
|
||||
|
||||
} // namespace beast
|
||||
@@ -105,9 +105,9 @@ public:
|
||||
[[nodiscard]] Buffer
|
||||
fingerprint() const override
|
||||
{
|
||||
sha256_hasher h;
|
||||
Sha256Hasher h;
|
||||
h(payload_.data(), payload_.size());
|
||||
auto const d = static_cast<sha256_hasher::result_type>(h);
|
||||
auto const d = static_cast<Sha256Hasher::result_type>(h);
|
||||
return {d.data(), d.size()};
|
||||
}
|
||||
|
||||
|
||||
@@ -288,12 +288,12 @@ checkConsensus(
|
||||
template <class Adaptor>
|
||||
class Consensus
|
||||
{
|
||||
using Ledger_t = Adaptor::Ledger_t;
|
||||
using TxSet_t = Adaptor::TxSet_t;
|
||||
using NodeID_t = Adaptor::NodeID_t;
|
||||
using Tx_t = TxSet_t::Tx;
|
||||
using PeerPosition_t = Adaptor::PeerPosition_t;
|
||||
using Proposal_t = ConsensusProposal<NodeID_t, typename Ledger_t::ID, typename TxSet_t::ID>;
|
||||
using LedgerT = Adaptor::LedgerT;
|
||||
using TxSetT = Adaptor::TxSetT;
|
||||
using NodeIDT = Adaptor::NodeIDT;
|
||||
using TxT = TxSetT::Tx;
|
||||
using PeerPositionT = Adaptor::PeerPositionT;
|
||||
using ProposalT = ConsensusProposal<NodeIDT, typename LedgerT::ID, typename TxSetT::ID>;
|
||||
|
||||
using Result = ConsensusResult<Adaptor>;
|
||||
|
||||
@@ -325,7 +325,7 @@ public:
|
||||
/**
|
||||
* Clock type for measuring time within the consensus code
|
||||
*/
|
||||
using clock_type = beast::AbstractClock<std::chrono::steady_clock>;
|
||||
using ClockType = beast::AbstractClock<std::chrono::steady_clock>;
|
||||
|
||||
Consensus(Consensus&&) noexcept = default;
|
||||
|
||||
@@ -336,7 +336,7 @@ public:
|
||||
* @param adaptor The instance of the adaptor class
|
||||
* @param j The journal to log debug output
|
||||
*/
|
||||
Consensus(clock_type const& clock, Adaptor& adaptor, beast::Journal j);
|
||||
Consensus(ClockType const& clock, Adaptor& adaptor, beast::Journal j);
|
||||
|
||||
/**
|
||||
* Kick-off the next round of consensus.
|
||||
@@ -357,9 +357,9 @@ public:
|
||||
void
|
||||
startRound(
|
||||
NetClock::time_point const& now,
|
||||
Ledger_t::ID const& prevLedgerID,
|
||||
Ledger_t prevLedger,
|
||||
hash_set<NodeID_t> const& nowUntrusted,
|
||||
LedgerT::ID const& prevLedgerID,
|
||||
LedgerT prevLedger,
|
||||
HashSet<NodeIDT> const& nowUntrusted,
|
||||
bool proposing,
|
||||
std::unique_ptr<std::stringstream> const& clog = {});
|
||||
|
||||
@@ -371,7 +371,7 @@ public:
|
||||
* @return Whether we should do delayed relay of this proposal.
|
||||
*/
|
||||
bool
|
||||
peerProposal(NetClock::time_point const& now, PeerPosition_t const& newProposal);
|
||||
peerProposal(NetClock::time_point const& now, PeerPositionT const& newProposal);
|
||||
|
||||
/**
|
||||
* Call periodically to drive consensus forward.
|
||||
@@ -391,7 +391,7 @@ public:
|
||||
* @param txSet the transaction set
|
||||
*/
|
||||
void
|
||||
gotTxSet(NetClock::time_point const& now, TxSet_t const& txSet);
|
||||
gotTxSet(NetClock::time_point const& now, TxSetT const& txSet);
|
||||
|
||||
/**
|
||||
* Simulate the consensus process without any network traffic.
|
||||
@@ -423,7 +423,7 @@ public:
|
||||
*
|
||||
* @return ID of previous ledger
|
||||
*/
|
||||
Ledger_t::ID
|
||||
LedgerT::ID
|
||||
prevLedgerID() const
|
||||
{
|
||||
return prevLedgerID_;
|
||||
@@ -450,14 +450,14 @@ private:
|
||||
void
|
||||
startRoundInternal(
|
||||
NetClock::time_point const& now,
|
||||
Ledger_t::ID const& prevLedgerID,
|
||||
Ledger_t const& prevLedger,
|
||||
LedgerT::ID const& prevLedgerID,
|
||||
LedgerT const& prevLedger,
|
||||
ConsensusMode mode,
|
||||
std::unique_ptr<std::stringstream> const& clog);
|
||||
|
||||
// Change our view of the previous ledger
|
||||
void
|
||||
handleWrongLedger(Ledger_t::ID const& lgrId, std::unique_ptr<std::stringstream> const& clog);
|
||||
handleWrongLedger(LedgerT::ID const& lgrId, std::unique_ptr<std::stringstream> const& clog);
|
||||
|
||||
/**
|
||||
* Check if our previous ledger matches the network's.
|
||||
@@ -479,7 +479,7 @@ private:
|
||||
* Handle a replayed or a new peer proposal.
|
||||
*/
|
||||
bool
|
||||
peerProposalInternal(NetClock::time_point const& now, PeerPosition_t const& newProposal);
|
||||
peerProposalInternal(NetClock::time_point const& now, PeerPositionT const& newProposal);
|
||||
|
||||
/**
|
||||
* Handle pre-close phase.
|
||||
@@ -542,12 +542,12 @@ private:
|
||||
|
||||
// Create disputes between our position and the provided one.
|
||||
void
|
||||
createDisputes(TxSet_t const& o, std::unique_ptr<std::stringstream> const& clog = {});
|
||||
createDisputes(TxSetT const& o, std::unique_ptr<std::stringstream> const& clog = {});
|
||||
|
||||
// Update our disputes given that this node has adopted a new position.
|
||||
// Will call createDisputes as needed.
|
||||
void
|
||||
updateDisputes(NodeID_t const& node, TxSet_t const& other);
|
||||
updateDisputes(NodeIDT const& node, TxSetT const& other);
|
||||
|
||||
// Revoke our outstanding proposal, if any, and cease proposing
|
||||
// until this round ends.
|
||||
@@ -566,7 +566,7 @@ private:
|
||||
bool firstRound_ = true;
|
||||
bool haveCloseTimeConsensus_ = false;
|
||||
|
||||
clock_type const& clock_;
|
||||
ClockType const& clock_;
|
||||
|
||||
// How long the consensus convergence has taken, expressed as
|
||||
// a percentage of the time that we expected it to take.
|
||||
@@ -594,12 +594,12 @@ private:
|
||||
// Non-peer (self) consensus data
|
||||
|
||||
// Last validated ledger ID provided to consensus
|
||||
Ledger_t::ID prevLedgerID_;
|
||||
LedgerT::ID prevLedgerID_;
|
||||
// Last validated ledger seen by consensus
|
||||
Ledger_t previousLedger_;
|
||||
LedgerT previousLedger_;
|
||||
|
||||
// Transaction Sets, indexed by hash of transaction tree
|
||||
hash_map<typename TxSet_t::ID, TxSet_t const> acquired_;
|
||||
HashMap<typename TxSetT::ID, TxSetT const> acquired_;
|
||||
|
||||
std::optional<Result> result_;
|
||||
ConsensusCloseTimes rawCloseTimes_;
|
||||
@@ -615,24 +615,24 @@ private:
|
||||
// Peer related consensus data
|
||||
|
||||
// Peer proposed positions for the current round
|
||||
hash_map<NodeID_t, PeerPosition_t> currPeerPositions_;
|
||||
HashMap<NodeIDT, PeerPositionT> currPeerPositions_;
|
||||
|
||||
// Recently received peer positions, available when transitioning between
|
||||
// ledgers or rounds
|
||||
hash_map<NodeID_t, std::deque<PeerPosition_t>> recentPeerPositions_;
|
||||
HashMap<NodeIDT, std::deque<PeerPositionT>> recentPeerPositions_;
|
||||
|
||||
// The number of proposers who participated in the last consensus round
|
||||
std::size_t prevProposers_ = 0;
|
||||
|
||||
// nodes that have bowed out of this consensus process
|
||||
hash_set<NodeID_t> deadNodes_;
|
||||
HashSet<NodeIDT> deadNodes_;
|
||||
|
||||
// Journal for debugging
|
||||
beast::Journal const j_;
|
||||
};
|
||||
|
||||
template <class Adaptor>
|
||||
Consensus<Adaptor>::Consensus(clock_type const& clock, Adaptor& adaptor, beast::Journal journal)
|
||||
Consensus<Adaptor>::Consensus(ClockType const& clock, Adaptor& adaptor, beast::Journal journal)
|
||||
: adaptor_(adaptor), clock_(clock), j_{journal}
|
||||
{
|
||||
JLOG(j_.debug()) << "Creating consensus object";
|
||||
@@ -642,9 +642,9 @@ template <class Adaptor>
|
||||
void
|
||||
Consensus<Adaptor>::startRound(
|
||||
NetClock::time_point const& now,
|
||||
Ledger_t::ID const& prevLedgerID,
|
||||
Ledger_t prevLedger,
|
||||
hash_set<NodeID_t> const& nowUntrusted,
|
||||
LedgerT::ID const& prevLedgerID,
|
||||
LedgerT prevLedger,
|
||||
HashSet<NodeIDT> const& nowUntrusted,
|
||||
bool proposing,
|
||||
std::unique_ptr<std::stringstream> const& clog)
|
||||
{
|
||||
@@ -660,7 +660,7 @@ Consensus<Adaptor>::startRound(
|
||||
prevCloseTime_ = rawCloseTimes_.self;
|
||||
}
|
||||
|
||||
for (NodeID_t const& n : nowUntrusted)
|
||||
for (NodeIDT const& n : nowUntrusted)
|
||||
recentPeerPositions_.erase(n);
|
||||
|
||||
ConsensusMode startMode = proposing ? ConsensusMode::Proposing : ConsensusMode::Observing;
|
||||
@@ -687,8 +687,8 @@ template <class Adaptor>
|
||||
void
|
||||
Consensus<Adaptor>::startRoundInternal(
|
||||
NetClock::time_point const& now,
|
||||
Ledger_t::ID const& prevLedgerID,
|
||||
Ledger_t const& prevLedger,
|
||||
LedgerT::ID const& prevLedgerID,
|
||||
LedgerT const& prevLedger,
|
||||
ConsensusMode mode,
|
||||
std::unique_ptr<std::stringstream> const& clog)
|
||||
{
|
||||
@@ -715,7 +715,7 @@ Consensus<Adaptor>::startRoundInternal(
|
||||
closeResolution_ = getNextLedgerTimeResolution(
|
||||
previousLedger_.closeTimeResolution(),
|
||||
previousLedger_.closeAgree(),
|
||||
previousLedger_.seq() + typename Ledger_t::Seq{1});
|
||||
previousLedger_.seq() + typename LedgerT::Seq{1});
|
||||
|
||||
playbackProposals();
|
||||
CLOG(clog) << "number of peer proposals,previous proposers: " << currPeerPositions_.size()
|
||||
@@ -731,7 +731,7 @@ Consensus<Adaptor>::startRoundInternal(
|
||||
|
||||
template <class Adaptor>
|
||||
bool
|
||||
Consensus<Adaptor>::peerProposal(NetClock::time_point const& now, PeerPosition_t const& newPeerPos)
|
||||
Consensus<Adaptor>::peerProposal(NetClock::time_point const& now, PeerPositionT const& newPeerPos)
|
||||
{
|
||||
JLOG(j_.debug()) << "PROPOSAL " << newPeerPos.render();
|
||||
auto const& peerID = newPeerPos.proposal().nodeID();
|
||||
@@ -752,7 +752,7 @@ template <class Adaptor>
|
||||
bool
|
||||
Consensus<Adaptor>::peerProposalInternal(
|
||||
NetClock::time_point const& now,
|
||||
PeerPosition_t const& newPeerPos)
|
||||
PeerPositionT const& newPeerPos)
|
||||
{
|
||||
// Nothing to do for now if we are currently working on a ledger
|
||||
if (phase_ == ConsensusPhase::Accepted)
|
||||
@@ -889,7 +889,7 @@ Consensus<Adaptor>::timerEntry(
|
||||
|
||||
template <class Adaptor>
|
||||
void
|
||||
Consensus<Adaptor>::gotTxSet(NetClock::time_point const& now, TxSet_t const& txSet)
|
||||
Consensus<Adaptor>::gotTxSet(NetClock::time_point const& now, TxSetT const& txSet)
|
||||
{
|
||||
// Nothing to do if we've finished work on a ledger
|
||||
if (phase_ == ConsensusPhase::Accepted)
|
||||
@@ -1053,7 +1053,7 @@ Consensus<Adaptor>::getJson(bool full) const
|
||||
template <class Adaptor>
|
||||
void
|
||||
Consensus<Adaptor>::handleWrongLedger(
|
||||
Ledger_t::ID const& lgrId,
|
||||
LedgerT::ID const& lgrId,
|
||||
std::unique_ptr<std::stringstream> const& clog)
|
||||
{
|
||||
CLOG(clog) << "handleWrongLedger. ";
|
||||
@@ -1491,11 +1491,11 @@ Consensus<Adaptor>::updateOurPositions(std::unique_ptr<std::stringstream> const&
|
||||
auto it = currPeerPositions_.begin();
|
||||
while (it != currPeerPositions_.end())
|
||||
{
|
||||
Proposal_t const& peerProp = it->second.proposal();
|
||||
ProposalT const& peerProp = it->second.proposal();
|
||||
if (peerProp.isStale(peerCutoff))
|
||||
{
|
||||
// peer's proposal is stale, so remove it
|
||||
NodeID_t const& peerID = peerProp.nodeID();
|
||||
NodeIDT const& peerID = peerProp.nodeID();
|
||||
JLOG(j_.warn()) << "Removing stale proposal from " << peerID;
|
||||
for (auto& dt : result_->disputes)
|
||||
dt.second.unVote(peerID);
|
||||
@@ -1511,11 +1511,11 @@ Consensus<Adaptor>::updateOurPositions(std::unique_ptr<std::stringstream> const&
|
||||
}
|
||||
|
||||
// This will stay unseated unless there are any changes
|
||||
std::optional<TxSet_t> ourNewSet;
|
||||
std::optional<TxSetT> ourNewSet;
|
||||
|
||||
// Update votes on disputed transactions
|
||||
{
|
||||
std::optional<typename TxSet_t::MutableTxSet> mutableSet;
|
||||
std::optional<typename TxSetT::MutableTxSet> mutableSet;
|
||||
for (auto& [txId, dispute] : result_->disputes)
|
||||
{
|
||||
// Because the threshold for inclusion increases,
|
||||
@@ -1645,7 +1645,7 @@ Consensus<Adaptor>::updateOurPositions(std::unique_ptr<std::stringstream> const&
|
||||
|
||||
for (auto const& [nodeId, peerPos] : currPeerPositions_)
|
||||
{
|
||||
Proposal_t const& p = peerPos.proposal();
|
||||
ProposalT const& p = peerPos.proposal();
|
||||
if (p.position() == newID)
|
||||
updateDisputes(nodeId, result_->txns);
|
||||
}
|
||||
@@ -1674,7 +1674,7 @@ Consensus<Adaptor>::haveConsensus(std::unique_ptr<std::stringstream> const& clog
|
||||
// Count number of agreements/disagreements with our position
|
||||
for (auto const& [nodeId, peerPos] : currPeerPositions_)
|
||||
{
|
||||
Proposal_t const& peerProp = peerPos.proposal();
|
||||
ProposalT const& peerProp = peerPos.proposal();
|
||||
if (peerProp.position() == ourPosition)
|
||||
{
|
||||
++agree;
|
||||
@@ -1789,7 +1789,7 @@ Consensus<Adaptor>::leaveConsensus(std::unique_ptr<std::stringstream> const& clo
|
||||
|
||||
template <class Adaptor>
|
||||
void
|
||||
Consensus<Adaptor>::createDisputes(TxSet_t const& o, std::unique_ptr<std::stringstream> const& clog)
|
||||
Consensus<Adaptor>::createDisputes(TxSetT const& o, std::unique_ptr<std::stringstream> const& clog)
|
||||
{
|
||||
// Cannot create disputes without our stance
|
||||
XRPL_ASSERT(result_, "xrpl::Consensus::createDisputes : result is set");
|
||||
@@ -1825,7 +1825,7 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o, std::unique_ptr<std::string
|
||||
(!inThisSet && !result_->txns.find(txId) && o.find(txId)),
|
||||
"xrpl::Consensus::createDisputes : has disputed transactions");
|
||||
|
||||
Tx_t const tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
|
||||
TxT const tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
|
||||
auto txID = tx.id();
|
||||
|
||||
if (result_->disputes.find(txID) != result_->disputes.end())
|
||||
@@ -1833,7 +1833,7 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o, std::unique_ptr<std::string
|
||||
|
||||
JLOG(j_.debug()) << "Transaction " << txID << " is disputed";
|
||||
|
||||
typename Result::Dispute_t dtx{
|
||||
typename Result::DisputeT dtx{
|
||||
tx,
|
||||
result_->txns.exists(txID),
|
||||
std::max(prevProposers_, currPeerPositions_.size()),
|
||||
@@ -1842,7 +1842,7 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o, std::unique_ptr<std::string
|
||||
// Update all of the available peer's votes on the disputed transaction
|
||||
for (auto const& [nodeId, peerPos] : currPeerPositions_)
|
||||
{
|
||||
Proposal_t const& peerProp = peerPos.proposal();
|
||||
ProposalT const& peerProp = peerPos.proposal();
|
||||
auto const cit = acquired_.find(peerProp.position());
|
||||
if (cit != acquired_.end() && dtx.setVote(nodeId, cit->second.exists(txID)))
|
||||
peerUnchangedCounter_ = 0;
|
||||
@@ -1858,7 +1858,7 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o, std::unique_ptr<std::string
|
||||
|
||||
template <class Adaptor>
|
||||
void
|
||||
Consensus<Adaptor>::updateDisputes(NodeID_t const& node, TxSet_t const& other)
|
||||
Consensus<Adaptor>::updateDisputes(NodeIDT const& node, TxSetT const& other)
|
||||
{
|
||||
// Cannot updateDisputes without our stance
|
||||
XRPL_ASSERT(result_, "xrpl::Consensus::updateDisputes : result is set");
|
||||
|
||||
@@ -235,7 +235,7 @@ public:
|
||||
/**
|
||||
* The digest for this proposal, used for signing purposes.
|
||||
*/
|
||||
uint256 const&
|
||||
UInt256 const&
|
||||
signingHash() const
|
||||
{
|
||||
if (!signingHash_)
|
||||
@@ -283,7 +283,7 @@ private:
|
||||
/**
|
||||
* The signing hash for this proposal
|
||||
*/
|
||||
mutable std::optional<uint256> signingHash_;
|
||||
mutable std::optional<UInt256> signingHash_;
|
||||
};
|
||||
|
||||
template <class NodeId, class LedgerId, class Position>
|
||||
|
||||
@@ -282,15 +282,15 @@ enum class ConsensusState {
|
||||
template <class Traits>
|
||||
struct ConsensusResult
|
||||
{
|
||||
using Ledger_t = Traits::Ledger_t;
|
||||
using TxSet_t = Traits::TxSet_t;
|
||||
using NodeID_t = Traits::NodeID_t;
|
||||
using LedgerT = Traits::LedgerT;
|
||||
using TxSetT = Traits::TxSetT;
|
||||
using NodeIDT = Traits::NodeIDT;
|
||||
|
||||
using Tx_t = TxSet_t::Tx;
|
||||
using Proposal_t = ConsensusProposal<NodeID_t, typename Ledger_t::ID, typename TxSet_t::ID>;
|
||||
using Dispute_t = DisputedTx<Tx_t, NodeID_t>;
|
||||
using TxT = TxSetT::Tx;
|
||||
using ProposalT = ConsensusProposal<NodeIDT, typename LedgerT::ID, typename TxSetT::ID>;
|
||||
using DisputeT = DisputedTx<TxT, NodeIDT>;
|
||||
|
||||
ConsensusResult(TxSet_t&& s, Proposal_t&& p) : txns{std::move(s)}, position{std::move(p)}
|
||||
ConsensusResult(TxSetT&& s, ProposalT&& p) : txns{std::move(s)}, position{std::move(p)}
|
||||
{
|
||||
XRPL_ASSERT(txns.id() == position.position(), "xrpl::ConsensusResult : valid inputs");
|
||||
}
|
||||
@@ -298,20 +298,20 @@ struct ConsensusResult
|
||||
/**
|
||||
* The set of transactions consensus agrees go in the ledger
|
||||
*/
|
||||
TxSet_t txns;
|
||||
TxSetT txns;
|
||||
|
||||
/**
|
||||
* Our proposed position on transactions/close time
|
||||
*/
|
||||
Proposal_t position;
|
||||
ProposalT position;
|
||||
|
||||
/**
|
||||
* Transactions which are under dispute with our peers
|
||||
*/
|
||||
hash_map<typename Tx_t::ID, Dispute_t> disputes;
|
||||
HashMap<typename TxT::ID, DisputeT> disputes;
|
||||
|
||||
// Set of TxSet ids we have already compared/created disputes
|
||||
hash_set<typename TxSet_t::ID> compares;
|
||||
HashSet<typename TxSetT::ID> compares;
|
||||
|
||||
// Measures the duration of the establish phase for this consensus round
|
||||
ConsensusTimer roundTime;
|
||||
|
||||
@@ -34,8 +34,8 @@ namespace xrpl {
|
||||
template <class Tx, class NodeId>
|
||||
class DisputedTx
|
||||
{
|
||||
using TxID_t = Tx::ID;
|
||||
using Map_t = boost::container::flat_map<NodeId, bool>;
|
||||
using TxIDT = Tx::ID;
|
||||
using MapT = boost::container::flat_map<NodeId, bool>;
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
/**
|
||||
* The unique id/hash of the disputed transaction.
|
||||
*/
|
||||
[[nodiscard]] TxID_t const&
|
||||
[[nodiscard]] TxIDT const&
|
||||
id() const
|
||||
{
|
||||
return tx_.id();
|
||||
@@ -202,7 +202,7 @@ private:
|
||||
int nays_{0}; //< Number of no votes
|
||||
bool ourVote_; //< Our vote (true is yes)
|
||||
Tx tx_; //< Transaction under dispute
|
||||
Map_t votes_; //< Map from NodeID to vote
|
||||
MapT votes_; //< Map from NodeID to vote
|
||||
/**
|
||||
* The number of rounds we've gone without changing our vote
|
||||
*/
|
||||
|
||||
@@ -312,28 +312,25 @@ class Validations
|
||||
mutable Mutex mutex_;
|
||||
|
||||
// Validations from currently listed and trusted nodes (partial and full)
|
||||
hash_map<NodeID, Validation> current_;
|
||||
HashMap<NodeID, Validation> current_;
|
||||
|
||||
// Used to enforce the largest validation invariant for the local node
|
||||
SeqEnforcer<Seq> localSeqEnforcer_;
|
||||
|
||||
// Sequence of the largest validation received from each node
|
||||
hash_map<NodeID, SeqEnforcer<Seq>> seqEnforcers_;
|
||||
HashMap<NodeID, SeqEnforcer<Seq>> seqEnforcers_;
|
||||
|
||||
/**
|
||||
* Validations from listed nodes, indexed by ledger id (partial and full)
|
||||
*/
|
||||
beast::aged_unordered_map<
|
||||
ID,
|
||||
hash_map<NodeID, Validation>,
|
||||
std::chrono::steady_clock,
|
||||
beast::Uhash<>>
|
||||
byLedger_;
|
||||
beast::
|
||||
AgedUnorderedMap<ID, HashMap<NodeID, Validation>, std::chrono::steady_clock, beast::Uhash<>>
|
||||
byLedger_;
|
||||
|
||||
// Partial and full validations indexed by sequence
|
||||
beast::aged_unordered_map<
|
||||
beast::AgedUnorderedMap<
|
||||
Seq,
|
||||
hash_map<NodeID, Validation>,
|
||||
HashMap<NodeID, Validation>,
|
||||
std::chrono::steady_clock,
|
||||
beast::Uhash<>>
|
||||
bySequence_;
|
||||
@@ -351,10 +348,10 @@ class Validations
|
||||
|
||||
// Last (validated) ledger successfully acquired. If in this map, it is
|
||||
// accounted for in the trie.
|
||||
hash_map<NodeID, Ledger> lastLedger_;
|
||||
HashMap<NodeID, Ledger> lastLedger_;
|
||||
|
||||
// Set of ledgers being acquired from the network
|
||||
hash_map<std::pair<Seq, ID>, hash_set<NodeID>> acquiring_;
|
||||
HashMap<std::pair<Seq, ID>, HashSet<NodeID>> acquiring_;
|
||||
|
||||
// Parameters to determine validation staleness
|
||||
ValidationParms const parms_;
|
||||
@@ -794,7 +791,7 @@ public:
|
||||
* @param removed Identifiers of nodes that are no longer trusted
|
||||
*/
|
||||
void
|
||||
trustChanged(hash_set<NodeID> const& added, hash_set<NodeID> const& removed)
|
||||
trustChanged(HashSet<NodeID> const& added, HashSet<NodeID> const& removed)
|
||||
{
|
||||
std::scoped_lock const lock{mutex_};
|
||||
|
||||
@@ -862,9 +859,9 @@ public:
|
||||
// fall back to majority over acquiring ledgers
|
||||
auto it = std::ranges::max_element(acquiring_, [](auto const& a, auto const& b) {
|
||||
std::pair<Seq, ID> const& aKey = a.first;
|
||||
typename hash_set<NodeID>::size_type const& aSize = a.second.size();
|
||||
typename HashSet<NodeID>::size_type const& aSize = a.second.size();
|
||||
std::pair<Seq, ID> const& bKey = b.first;
|
||||
typename hash_set<NodeID>::size_type const& bSize = b.second.size();
|
||||
typename HashSet<NodeID>::size_type const& bSize = b.second.size();
|
||||
// order by number of trusted peers validating that ledger
|
||||
// break ties with ledger ID
|
||||
return std::tie(aSize, aKey.second) < std::tie(bSize, bKey.second);
|
||||
@@ -930,7 +927,7 @@ public:
|
||||
* does not know their sequence number
|
||||
*/
|
||||
ID
|
||||
getPreferredLCL(Ledger const& lcl, Seq minSeq, hash_map<ID, std::uint32_t> const& peerCounts)
|
||||
getPreferredLCL(Ledger const& lcl, Seq minSeq, HashMap<ID, std::uint32_t> const& peerCounts)
|
||||
{
|
||||
std::optional<std::pair<Seq, ID>> preferred = getPreferred(lcl);
|
||||
|
||||
@@ -1009,9 +1006,9 @@ public:
|
||||
* @return The set of node ids for active, listed validators
|
||||
*/
|
||||
auto
|
||||
getCurrentNodeIDs() -> hash_set<NodeID>
|
||||
getCurrentNodeIDs() -> HashSet<NodeID>
|
||||
{
|
||||
hash_set<NodeID> ret;
|
||||
HashSet<NodeID> ret;
|
||||
std::scoped_lock const lock{mutex_};
|
||||
current(
|
||||
lock,
|
||||
@@ -1127,7 +1124,7 @@ public:
|
||||
* @return Quantity of laggards.
|
||||
*/
|
||||
std::size_t
|
||||
laggards(Seq const seq, hash_set<NodeKey>& trustedKeys)
|
||||
laggards(Seq const seq, HashSet<NodeKey>& trustedKeys)
|
||||
{
|
||||
std::size_t laggards = 0;
|
||||
|
||||
|
||||
@@ -0,0 +1,713 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <coroutine>
|
||||
#include <exception>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename T = void>
|
||||
class CoroTask;
|
||||
|
||||
/**
|
||||
* CoroTask<void> -- coroutine return type for void-returning coroutines.
|
||||
*
|
||||
* Class / Dependency Diagram
|
||||
* ==========================
|
||||
*
|
||||
* CoroTask<void>
|
||||
* +-----------------------------------------------+
|
||||
* | - handle_ : Handle (coroutine_handle<promise>) |
|
||||
* +-----------------------------------------------+
|
||||
* | + handle(), done() |
|
||||
* | + await_ready/suspend/resume (Awaiter iface) |
|
||||
* +-----------------------------------------------+
|
||||
* | owns
|
||||
* v
|
||||
* promise_type
|
||||
* +-----------------------------------------------+
|
||||
* | - exception_ : std::exception_ptr |
|
||||
* | - continuation_ : std::coroutine_handle<> |
|
||||
* +-----------------------------------------------+
|
||||
* | + get_return_object() -> CoroTask |
|
||||
* | + initial_suspend() -> suspend_always (lazy) |
|
||||
* | + final_suspend() -> FinalAwaiter |
|
||||
* | + return_void() |
|
||||
* | + unhandled_exception() |
|
||||
* +-----------------------------------------------+
|
||||
* | returns at final_suspend
|
||||
* v
|
||||
* FinalAwaiter
|
||||
* +-----------------------------------------------+
|
||||
* | await_suspend(h): |
|
||||
* | if continuation_ set -> symmetric transfer |
|
||||
* | else -> noop_coroutine |
|
||||
* +-----------------------------------------------+
|
||||
*
|
||||
* Design Notes
|
||||
* ------------
|
||||
* - Lazy start: initial_suspend returns suspend_always, so the coroutine
|
||||
* body does not execute until the handle is explicitly resumed.
|
||||
* - Symmetric transfer: await_suspend returns a coroutine_handle instead
|
||||
* of void/bool, allowing the scheduler to jump directly to the next
|
||||
* coroutine without growing the call stack.
|
||||
* - Continuation chaining: when one CoroTask is co_await-ed inside
|
||||
* another, the caller's handle is stored as continuation_ so
|
||||
* FinalAwaiter can resume it when this task finishes.
|
||||
* - Move-only: the handle is exclusively owned; copy is deleted.
|
||||
*
|
||||
* Usage Examples
|
||||
* ==============
|
||||
*
|
||||
* 1. Basic void coroutine (the most common case in xrpld):
|
||||
*
|
||||
* CoroTask<void> doWork(std::shared_ptr<CoroTaskRunner> runner) {
|
||||
* // do something
|
||||
* co_await runner->suspend(); // yield control
|
||||
* // resumed later via runner->post() or runner->resume()
|
||||
* co_return;
|
||||
* }
|
||||
*
|
||||
* 2. co_await-ing one CoroTask<void> from another (chaining):
|
||||
*
|
||||
* CoroTask<void> inner() {
|
||||
* // ...
|
||||
* co_return;
|
||||
* }
|
||||
* CoroTask<void> outer() {
|
||||
* co_await inner(); // continuation_ links outer -> inner
|
||||
* co_return; // FinalAwaiter resumes outer
|
||||
* }
|
||||
*
|
||||
* 3. Exceptions propagate through co_await:
|
||||
*
|
||||
* CoroTask<void> failing() {
|
||||
* throw std::runtime_error("oops");
|
||||
* co_return;
|
||||
* }
|
||||
* CoroTask<void> caller() {
|
||||
* try { co_await failing(); }
|
||||
* catch (std::runtime_error const&) { // caught here }
|
||||
* }
|
||||
*
|
||||
* Caveats / Pitfalls
|
||||
* ==================
|
||||
*
|
||||
* BUG-RISK: Dangling references in coroutine parameters.
|
||||
* Coroutine parameters are copied into the frame, but references
|
||||
* are NOT -- they are stored as-is. If the referent goes out of scope
|
||||
* before the coroutine finishes, you get use-after-free.
|
||||
*
|
||||
* // BROKEN -- local dies before coroutine runs:
|
||||
* CoroTask<void> bad(int& ref) { co_return; }
|
||||
* void launch() {
|
||||
* int local = 42;
|
||||
* auto task = bad(local); // frame stores &local
|
||||
* } // local destroyed; frame holds dangling ref
|
||||
*
|
||||
* // FIX -- pass by value, or ensure lifetime via shared_ptr.
|
||||
*
|
||||
* BUG-RISK: GCC 14 corrupts reference captures in coroutine lambdas.
|
||||
* When a lambda that returns CoroTask captures by reference ([&]),
|
||||
* GCC 14 may generate a corrupted coroutine frame. Always capture
|
||||
* by explicit pointer-to-value instead:
|
||||
*
|
||||
* // BROKEN on GCC 14:
|
||||
* jq.postCoroTask(t, n, [&](auto) -> CoroTask<void> { ... });
|
||||
*
|
||||
* // FIX -- capture pointers explicitly:
|
||||
* jq.postCoroTask(t, n, [ptr = &val](auto) -> CoroTask<void> { ... });
|
||||
*
|
||||
* BUG-RISK: Resuming a destroyed or completed CoroTask.
|
||||
* Calling handle().resume() after the coroutine has already run to
|
||||
* completion (done() == true) is undefined behavior. The CoroTaskRunner
|
||||
* guards against this with an XRPL_ASSERT, but standalone usage of
|
||||
* CoroTask must check done() before resuming.
|
||||
*
|
||||
* BUG-RISK: Moving a CoroTask that is being awaited.
|
||||
* If task A is co_await-ed by task B (so A.continuation_ == B), moving
|
||||
* or destroying A will invalidate the continuation link. Never move
|
||||
* or reassign a CoroTask while it is mid-execution or being awaited.
|
||||
*
|
||||
* LIMITATION: CoroTask is fire-and-forget for the top-level owner.
|
||||
* There is no built-in notification when the coroutine finishes.
|
||||
* The caller must use external synchronization (e.g. CoroTaskRunner::join
|
||||
* or a gate/condition_variable) to know when it is done.
|
||||
*
|
||||
* LIMITATION: No cancellation token.
|
||||
* There is no way to cancel a suspended CoroTask from outside. The
|
||||
* coroutine body must cooperatively check a flag (e.g. jq_.isStopping())
|
||||
* after each co_await and co_return early if needed.
|
||||
*
|
||||
* LIMITATION: Stackless -- cannot suspend from nested non-coroutine calls.
|
||||
* If a coroutine calls a regular function that wants to "yield", it
|
||||
* cannot. Only the immediate coroutine body can use co_await.
|
||||
* This is acceptable for xrpld because all yield() sites are shallow.
|
||||
*/
|
||||
template <>
|
||||
class CoroTask<void>
|
||||
{
|
||||
public:
|
||||
// The C++ coroutine protocol mandates these names (promise_type,
|
||||
// initial_suspend, await_ready, ...) and instance-callable awaiter
|
||||
// methods, which conflict with the project naming/static conventions.
|
||||
// NOLINTBEGIN(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
struct promise_type;
|
||||
using Handle = std::coroutine_handle<promise_type>;
|
||||
|
||||
/**
|
||||
* Coroutine promise. Compiler uses this to manage coroutine state.
|
||||
* Stores the exception (if any) and the continuation handle for
|
||||
* symmetric transfer back to the awaiting coroutine.
|
||||
*/
|
||||
struct promise_type
|
||||
{
|
||||
// Captured exception from the coroutine body, rethrown in
|
||||
// await_resume() when this task is co_await-ed by a caller.
|
||||
std::exception_ptr exception_;
|
||||
|
||||
// Handle to the coroutine that is co_await-ing this task.
|
||||
// Set by await_suspend(). FinalAwaiter uses it for symmetric
|
||||
// transfer back to the caller. Null if this is a top-level task.
|
||||
std::coroutine_handle<> continuation_;
|
||||
|
||||
/**
|
||||
* Create the CoroTask return object.
|
||||
* Called by the compiler at coroutine creation.
|
||||
*/
|
||||
CoroTask
|
||||
get_return_object()
|
||||
{
|
||||
return CoroTask{Handle::from_promise(*this)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy start. The coroutine body does not execute until the
|
||||
* handle is explicitly resumed (e.g. by CoroTaskRunner::resume).
|
||||
*/
|
||||
std::suspend_always
|
||||
initial_suspend() noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaiter returned by final_suspend(). Uses symmetric transfer:
|
||||
* if a continuation exists, transfers control directly to it
|
||||
* (tail-call, no stack growth). Otherwise returns noop_coroutine
|
||||
* so the coroutine frame stays alive for the owner to destroy.
|
||||
*/
|
||||
struct FinalAwaiter
|
||||
{
|
||||
/**
|
||||
* Always false. We need await_suspend to run for
|
||||
* symmetric transfer.
|
||||
*/
|
||||
bool
|
||||
await_ready() noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric transfer: returns the continuation handle so
|
||||
* the compiler emits a tail-call instead of a nested resume.
|
||||
* If no continuation is set, returns noop_coroutine to
|
||||
* suspend at final_suspend without destroying the frame.
|
||||
*
|
||||
* @param h Handle to this completing coroutine
|
||||
*
|
||||
* @return Continuation handle, or noop_coroutine
|
||||
*/
|
||||
std::coroutine_handle<>
|
||||
await_suspend(Handle h) noexcept
|
||||
{
|
||||
if (auto cont = h.promise().continuation_)
|
||||
return cont;
|
||||
return std::noop_coroutine();
|
||||
}
|
||||
|
||||
void
|
||||
await_resume() noexcept
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns FinalAwaiter for symmetric transfer at coroutine end.
|
||||
*/
|
||||
FinalAwaiter
|
||||
final_suspend() noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the compiler for `co_return;` (void coroutine).
|
||||
*/
|
||||
void
|
||||
return_void()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the compiler when an exception escapes the coroutine
|
||||
* body. Captures it for later rethrowing in await_resume().
|
||||
*/
|
||||
void
|
||||
unhandled_exception()
|
||||
{
|
||||
exception_ = std::current_exception();
|
||||
}
|
||||
};
|
||||
// NOLINTEND(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
|
||||
/**
|
||||
* Default constructor. Creates an empty (null handle) task.
|
||||
*/
|
||||
CoroTask() = default;
|
||||
|
||||
/**
|
||||
* Takes ownership of a compiler-generated coroutine handle.
|
||||
*
|
||||
* @param h Coroutine handle to own
|
||||
*/
|
||||
explicit CoroTask(Handle h) : handle_(h)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the coroutine frame if this task owns one.
|
||||
*/
|
||||
~CoroTask()
|
||||
{
|
||||
if (handle_)
|
||||
handle_.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move constructor. Transfers handle ownership, leaves other empty.
|
||||
*/
|
||||
CoroTask(CoroTask&& other) noexcept : handle_(std::exchange(other.handle_, {}))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Move assignment. Destroys current frame (if any), takes other's.
|
||||
*/
|
||||
CoroTask&
|
||||
operator=(CoroTask&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if (handle_)
|
||||
handle_.destroy();
|
||||
handle_ = std::exchange(other.handle_, {});
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CoroTask(CoroTask const&) = delete;
|
||||
CoroTask&
|
||||
operator=(CoroTask const&) = delete;
|
||||
|
||||
/**
|
||||
* @return The underlying coroutine_handle
|
||||
*/
|
||||
[[nodiscard]] Handle
|
||||
handle() const
|
||||
{
|
||||
return handle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the coroutine has run to completion (or thrown)
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
done() const
|
||||
{
|
||||
return handle_ && handle_.done();
|
||||
}
|
||||
|
||||
// -- Awaiter interface: allows `co_await someCoroTask;` --
|
||||
|
||||
/**
|
||||
* Always false. This task is lazy, so co_await always suspends
|
||||
* the caller to set up the continuation link.
|
||||
*/
|
||||
// NOLINTBEGIN(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
[[nodiscard]] bool
|
||||
await_ready() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the caller's handle as our continuation, then returns
|
||||
* our handle for symmetric transfer (caller suspends, we resume).
|
||||
*
|
||||
* @param caller Handle of the coroutine doing co_await on us
|
||||
*
|
||||
* @return Our handle for symmetric transfer
|
||||
*/
|
||||
std::coroutine_handle<>
|
||||
await_suspend(std::coroutine_handle<> caller) noexcept
|
||||
{
|
||||
XRPL_ASSERT(handle_, "xrpl::CoroTask<void>::await_suspend : handle is valid");
|
||||
handle_.promise().continuation_ = caller;
|
||||
return handle_; // Symmetric transfer
|
||||
}
|
||||
|
||||
/**
|
||||
* Called in the awaiting coroutine's context after this task
|
||||
* completes. Rethrows any exception captured by
|
||||
* unhandled_exception().
|
||||
*/
|
||||
void
|
||||
await_resume()
|
||||
{
|
||||
XRPL_ASSERT(handle_, "xrpl::CoroTask<void>::await_resume : handle is valid");
|
||||
if (auto& ep = handle_.promise().exception_)
|
||||
std::rethrow_exception(ep);
|
||||
}
|
||||
// NOLINTEND(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
|
||||
private:
|
||||
// Exclusively-owned coroutine handle. Null after move or default
|
||||
// construction. Destroyed in the destructor.
|
||||
Handle handle_;
|
||||
};
|
||||
|
||||
/**
|
||||
* CoroTask<T> -- coroutine return type for value-returning coroutines.
|
||||
*
|
||||
* Class / Dependency Diagram
|
||||
* ==========================
|
||||
*
|
||||
* CoroTask<T>
|
||||
* +-----------------------------------------------+
|
||||
* | - handle_ : Handle (coroutine_handle<promise>) |
|
||||
* +-----------------------------------------------+
|
||||
* | + handle(), done() |
|
||||
* | + await_ready/suspend/resume (Awaiter iface) |
|
||||
* +-----------------------------------------------+
|
||||
* | owns
|
||||
* v
|
||||
* promise_type
|
||||
* +-----------------------------------------------+
|
||||
* | - result_ : variant<monostate, T, |
|
||||
* | exception_ptr> |
|
||||
* | - continuation_ : std::coroutine_handle<> |
|
||||
* +-----------------------------------------------+
|
||||
* | + get_return_object() -> CoroTask |
|
||||
* | + initial_suspend() -> suspend_always (lazy) |
|
||||
* | + final_suspend() -> FinalAwaiter |
|
||||
* | + return_value(T) -> stores in result_[1] |
|
||||
* | + unhandled_exception -> stores in result_[2] |
|
||||
* +-----------------------------------------------+
|
||||
* | returns at final_suspend
|
||||
* v
|
||||
* FinalAwaiter (same symmetric-transfer pattern as CoroTask<void>)
|
||||
*
|
||||
* Value Extraction
|
||||
* ----------------
|
||||
* await_resume() inspects the variant:
|
||||
* - index 2 (exception_ptr) -> rethrow
|
||||
* - index 1 (T) -> return value via move
|
||||
*
|
||||
* Usage Examples
|
||||
* ==============
|
||||
*
|
||||
* 1. Simple value return:
|
||||
*
|
||||
* CoroTask<int> computeAnswer() { co_return 42; }
|
||||
*
|
||||
* CoroTask<void> caller() {
|
||||
* int v = co_await computeAnswer(); // v == 42
|
||||
* }
|
||||
*
|
||||
* 2. Chaining value-returning coroutines:
|
||||
*
|
||||
* CoroTask<int> add(int a, int b) { co_return a + b; }
|
||||
* CoroTask<int> doubleSum(int a, int b) {
|
||||
* int s = co_await add(a, b);
|
||||
* co_return s * 2;
|
||||
* }
|
||||
*
|
||||
* 3. Exception propagation from inner to outer:
|
||||
*
|
||||
* CoroTask<int> failing() {
|
||||
* throw std::runtime_error("bad");
|
||||
* co_return 0; // never reached
|
||||
* }
|
||||
* CoroTask<void> caller() {
|
||||
* try {
|
||||
* int v = co_await failing(); // throws here
|
||||
* } catch (std::runtime_error const& e) {
|
||||
* // e.what() == "bad"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Caveats / Pitfalls (in addition to CoroTask<void> caveats above)
|
||||
* ================================================================
|
||||
*
|
||||
* BUG-RISK: await_resume() moves the value out of the variant.
|
||||
* Calling co_await on the same CoroTask<T> instance twice is undefined
|
||||
* behavior -- the second call will see a moved-from T. CoroTask is
|
||||
* single-shot: one co_return, one co_await.
|
||||
*
|
||||
* BUG-RISK: T must be move-constructible.
|
||||
* return_value(T) takes by value and moves into the variant.
|
||||
* Types that are not movable cannot be used as T.
|
||||
*
|
||||
* LIMITATION: No co_yield support.
|
||||
* CoroTask<T> only supports a single co_return. It does not implement
|
||||
* yield_value(), so using co_yield inside a CoroTask<T> coroutine is a
|
||||
* compile error. For streaming values, a different return type
|
||||
* (e.g. Generator<T>) would be needed.
|
||||
*
|
||||
* LIMITATION: Result is only accessible via co_await.
|
||||
* There is no .get() or .result() method. The value can only be
|
||||
* extracted by co_await-ing the CoroTask<T> from inside another
|
||||
* coroutine. For extracting results in non-coroutine code, pass a
|
||||
* pointer to the caller and write through it (as the tests do).
|
||||
*/
|
||||
template <typename T>
|
||||
class CoroTask
|
||||
{
|
||||
static_assert(
|
||||
std::is_move_constructible_v<T>,
|
||||
"CoroTask<T> requires T to be move-constructible");
|
||||
|
||||
public:
|
||||
// The C++ coroutine protocol mandates these names (promise_type,
|
||||
// initial_suspend, await_ready, ...) and instance-callable awaiter
|
||||
// methods, which conflict with the project naming/static conventions.
|
||||
// NOLINTBEGIN(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
struct promise_type;
|
||||
using Handle = std::coroutine_handle<promise_type>;
|
||||
|
||||
/**
|
||||
* Coroutine promise for value-returning coroutines.
|
||||
* Stores the result as a variant: monostate (not yet set),
|
||||
* T (co_return value), or exception_ptr (unhandled exception).
|
||||
*/
|
||||
struct promise_type
|
||||
{
|
||||
// Tri-state result:
|
||||
// index 0 (monostate) -- coroutine has not yet completed
|
||||
// index 1 (T) -- co_return value stored here
|
||||
// index 2 (exception) -- unhandled exception captured here
|
||||
std::variant<std::monostate, T, std::exception_ptr> result_;
|
||||
|
||||
// Handle to the coroutine co_await-ing this task. Used by
|
||||
// FinalAwaiter for symmetric transfer. Null for top-level tasks.
|
||||
std::coroutine_handle<> continuation_;
|
||||
|
||||
/**
|
||||
* Create the CoroTask return object.
|
||||
* Called by the compiler at coroutine creation.
|
||||
*/
|
||||
CoroTask
|
||||
get_return_object()
|
||||
{
|
||||
return CoroTask{Handle::from_promise(*this)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy start. Coroutine body does not run until explicitly resumed.
|
||||
*/
|
||||
std::suspend_always
|
||||
initial_suspend() noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric-transfer awaiter at coroutine completion.
|
||||
* Same pattern as CoroTask<void>::FinalAwaiter.
|
||||
*/
|
||||
struct FinalAwaiter
|
||||
{
|
||||
bool
|
||||
await_ready() noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns continuation for symmetric transfer, or
|
||||
* noop_coroutine if this is a top-level task.
|
||||
*
|
||||
* @param h Handle to this completing coroutine
|
||||
*
|
||||
* @return Continuation handle, or noop_coroutine
|
||||
*/
|
||||
std::coroutine_handle<>
|
||||
await_suspend(Handle h) noexcept
|
||||
{
|
||||
if (auto cont = h.promise().continuation_)
|
||||
return cont;
|
||||
return std::noop_coroutine();
|
||||
}
|
||||
|
||||
void
|
||||
await_resume() noexcept
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
FinalAwaiter
|
||||
final_suspend() noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the compiler for `co_return value;`.
|
||||
* Moves the value into result_ at index 1.
|
||||
*
|
||||
* @param value The value to store
|
||||
*/
|
||||
void
|
||||
return_value(T value)
|
||||
{
|
||||
result_.template emplace<1>(std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures unhandled exceptions at index 2 of result_.
|
||||
* Rethrown later in await_resume().
|
||||
*/
|
||||
void
|
||||
unhandled_exception()
|
||||
{
|
||||
result_.template emplace<2>(std::current_exception());
|
||||
}
|
||||
};
|
||||
// NOLINTEND(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
|
||||
/**
|
||||
* Default constructor. Creates an empty (null handle) task.
|
||||
*/
|
||||
CoroTask() = default;
|
||||
|
||||
/**
|
||||
* Takes ownership of a compiler-generated coroutine handle.
|
||||
*
|
||||
* @param h Coroutine handle to own
|
||||
*/
|
||||
explicit CoroTask(Handle h) : handle_(h)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the coroutine frame if this task owns one.
|
||||
*/
|
||||
~CoroTask()
|
||||
{
|
||||
if (handle_)
|
||||
handle_.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move constructor. Transfers handle ownership, leaves other empty.
|
||||
*/
|
||||
CoroTask(CoroTask&& other) noexcept : handle_(std::exchange(other.handle_, {}))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Move assignment. Destroys current frame (if any), takes other's.
|
||||
*/
|
||||
CoroTask&
|
||||
operator=(CoroTask&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if (handle_)
|
||||
handle_.destroy();
|
||||
handle_ = std::exchange(other.handle_, {});
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CoroTask(CoroTask const&) = delete;
|
||||
CoroTask&
|
||||
operator=(CoroTask const&) = delete;
|
||||
|
||||
/**
|
||||
* @return The underlying coroutine_handle
|
||||
*/
|
||||
[[nodiscard]] Handle
|
||||
handle() const
|
||||
{
|
||||
return handle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the coroutine has run to completion (or thrown)
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
done() const
|
||||
{
|
||||
return handle_ && handle_.done();
|
||||
}
|
||||
|
||||
// -- Awaiter interface: allows `T val = co_await someCoroTask;` --
|
||||
|
||||
/**
|
||||
* Always false. co_await always suspends to set up continuation.
|
||||
*/
|
||||
// NOLINTBEGIN(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
[[nodiscard]] bool
|
||||
await_ready() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores caller as continuation, returns our handle for
|
||||
* symmetric transfer.
|
||||
*
|
||||
* @param caller Handle of the coroutine doing co_await on us
|
||||
*
|
||||
* @return Our handle for symmetric transfer
|
||||
*/
|
||||
std::coroutine_handle<>
|
||||
await_suspend(std::coroutine_handle<> caller) noexcept
|
||||
{
|
||||
XRPL_ASSERT(handle_, "xrpl::CoroTask<T>::await_suspend : handle is valid");
|
||||
handle_.promise().continuation_ = caller;
|
||||
return handle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the result: rethrows if exception, otherwise moves
|
||||
* the T value out of the variant. Single-shot: calling twice
|
||||
* on the same task is undefined (moved-from T).
|
||||
*
|
||||
* @return The co_return-ed value
|
||||
*/
|
||||
T
|
||||
await_resume()
|
||||
{
|
||||
XRPL_ASSERT(handle_, "xrpl::CoroTask<T>::await_resume : handle is valid");
|
||||
auto& result = handle_.promise().result_;
|
||||
if (auto* ep = std::get_if<2>(&result))
|
||||
std::rethrow_exception(*ep);
|
||||
return std::get<1>(std::move(result));
|
||||
}
|
||||
// NOLINTEND(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
|
||||
private:
|
||||
// Exclusively-owned coroutine handle. Null after move or default
|
||||
// construction. Destroyed in the destructor.
|
||||
Handle handle_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,448 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file CoroTaskRunner.ipp
|
||||
*
|
||||
* CoroTaskRunner inline implementation.
|
||||
*
|
||||
* This file contains the business logic for managing C++20 coroutines
|
||||
* on the JobQueue. It is included at the bottom of JobQueue.h.
|
||||
*
|
||||
* Data Flow: suspend / post / resume cycle
|
||||
* =========================================
|
||||
*
|
||||
* coroutine body CoroTaskRunner JobQueue
|
||||
* -------------- -------------- --------
|
||||
* |
|
||||
* co_await runner->suspend()
|
||||
* |
|
||||
* +--- await_suspend ------> onSuspend()
|
||||
* | ++nSuspend_ ------------> nSuspend_
|
||||
* | [coroutine is now suspended]
|
||||
* |
|
||||
* . (externally or by yieldAndPost())
|
||||
* .
|
||||
* +--- (caller calls) -----> post()
|
||||
* | ++runCount_
|
||||
* | addJob(resume) ----------> job enqueued
|
||||
* | |
|
||||
* | [worker picks up]
|
||||
* | |
|
||||
* +--- <----- resume() <-----------------------------------+
|
||||
* | --nSuspend_ ------> nSuspend_
|
||||
* | swap in LocalValues (lvs_)
|
||||
* | task_.handle().resume()
|
||||
* | |
|
||||
* | [coroutine body continues here]
|
||||
* | |
|
||||
* | swap out LocalValues
|
||||
* | --runCount_
|
||||
* | cv_.notify_all()
|
||||
* v
|
||||
*
|
||||
* Thread Safety
|
||||
* =============
|
||||
* - mutex_ : guards task_.handle().resume() so that post()-before-suspend
|
||||
* races cannot resume the coroutine while it is still running.
|
||||
* (See the race condition discussion in JobQueue.h)
|
||||
* - mutexRun_ : guards runCount_ counter; used by join() to wait until
|
||||
* all in-flight resume operations complete.
|
||||
* - jq_.mutex_: guards nSuspend_ increments/decrements.
|
||||
*
|
||||
* Common Mistakes When Modifying This File
|
||||
* =========================================
|
||||
*
|
||||
* 1. Changing lock ordering.
|
||||
* resume() acquires locks sequentially (never held simultaneously):
|
||||
* jq_.mutex_ (released immediately), then mutex_ (held across resume),
|
||||
* then mutexRun_ (released after decrement). post() acquires only
|
||||
* mutexRun_. Any new code path must follow the same order.
|
||||
*
|
||||
* 2. Removing the shared_from_this() capture in post().
|
||||
* The lambda passed to addJob captures [this, sp = shared_from_this()].
|
||||
* If you remove sp, 'this' can be destroyed before the job runs,
|
||||
* causing use-after-free. The sp capture is load-bearing.
|
||||
*
|
||||
* 3. Forgetting to decrement nSuspend_ on a new code path.
|
||||
* Every ++nSuspend_ must have a matching --nSuspend_. If you add a new
|
||||
* suspension path (e.g. a new awaiter) and forget to decrement on resume
|
||||
* or on failure, JobQueue::stop() will hang.
|
||||
*
|
||||
* 4. Calling task_.handle().resume() without holding mutex_.
|
||||
* This allows a race where the coroutine runs on two threads
|
||||
* simultaneously. Always hold mutex_ around resume().
|
||||
*
|
||||
* 5. Swapping LocalValues outside of the mutex_ critical section.
|
||||
* The swap-in and swap-out of LocalValues must bracket the resume()
|
||||
* call. If you move the swap-out before the lock_guard(mutex_) is
|
||||
* released, you break LocalValue isolation for any code that runs
|
||||
* after the coroutine suspends but before the lock is dropped.
|
||||
*/
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Construct a CoroTaskRunner. Sets runCount_ to 0; does not
|
||||
* create the coroutine. Call init() afterwards.
|
||||
*
|
||||
* @param jq The JobQueue this coroutine will run on
|
||||
* @param type Job type for scheduling priority
|
||||
* @param name Human-readable name for logging
|
||||
*/
|
||||
inline JobQueue::CoroTaskRunner::CoroTaskRunner(
|
||||
CreateT,
|
||||
JobQueue& jq,
|
||||
JobType type,
|
||||
std::string name)
|
||||
: jq_(jq), type_(type), name_(std::move(name))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize with a coroutine-returning callable.
|
||||
* Stores the callable on the heap (FuncStore) so it outlives the
|
||||
* coroutine frame. Coroutine frames store a reference to the
|
||||
* callable's implicit object parameter (the lambda). If the callable
|
||||
* is a temporary, that reference dangles after the caller returns.
|
||||
* Keeping the callable alive here ensures the coroutine's captures
|
||||
* remain valid.
|
||||
*
|
||||
* @param f Callable: CoroTask<void>(shared_ptr<CoroTaskRunner>)
|
||||
*/
|
||||
template <class F>
|
||||
void
|
||||
JobQueue::CoroTaskRunner::init(F&& f)
|
||||
{
|
||||
using Fn = std::decay_t<F>;
|
||||
auto store = std::make_unique<FuncStore<Fn>>(std::forward<F>(f));
|
||||
task_ = store->func(shared_from_this());
|
||||
storedFunc_ = std::move(store);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor. Waits for any in-flight resume() to complete, then
|
||||
* asserts (debug) that the coroutine has finished or
|
||||
* expectEarlyExit() was called.
|
||||
*
|
||||
* The join() call is necessary because with async dispatch the
|
||||
* coroutine runs on a worker thread. The gate signal (which wakes
|
||||
* the test thread) can arrive before resume() has set finished_.
|
||||
* join() synchronizes via mutexRun_, establishing a happens-before
|
||||
* edge: finished_ = true -> unlock(mutexRun_) in resume() ->
|
||||
* lock(mutexRun_) in join() -> read finished_.
|
||||
*/
|
||||
inline JobQueue::CoroTaskRunner::~CoroTaskRunner()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
join();
|
||||
XRPL_ASSERT(finished_, "xrpl::JobQueue::CoroTaskRunner::~CoroTaskRunner : is finished");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the JobQueue's suspended-coroutine count (nSuspend_).
|
||||
*/
|
||||
inline void
|
||||
JobQueue::CoroTaskRunner::onSuspend()
|
||||
{
|
||||
std::scoped_lock const lock(jq_.mutex_);
|
||||
++jq_.nSuspend_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement nSuspend_ without resuming.
|
||||
*/
|
||||
inline void
|
||||
JobQueue::CoroTaskRunner::onUndoSuspend()
|
||||
{
|
||||
std::scoped_lock const lock(jq_.mutex_);
|
||||
--jq_.nSuspend_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a SuspendAwaiter whose await_suspend() increments nSuspend_
|
||||
* before the coroutine actually suspends. The caller must later call
|
||||
* post() or resume() to continue execution.
|
||||
*
|
||||
* @return Awaiter for use with `co_await runner->suspend()`
|
||||
*/
|
||||
inline auto
|
||||
JobQueue::CoroTaskRunner::suspend()
|
||||
{
|
||||
/**
|
||||
* Custom awaiter for suspend(). Always suspends (await_ready
|
||||
* returns false) and increments nSuspend_ in await_suspend().
|
||||
*/
|
||||
// The C++ coroutine protocol mandates these awaiter names and
|
||||
// instance-callable methods, which conflict with the project
|
||||
// naming/static conventions.
|
||||
// NOLINTBEGIN(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
struct SuspendAwaiter
|
||||
{
|
||||
CoroTaskRunner& runner_; // The runner that owns this coroutine.
|
||||
|
||||
/**
|
||||
* Always returns false so the coroutine suspends.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
await_ready() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the coroutine suspends. Increments nSuspend_
|
||||
* so the JobQueue knows a coroutine is waiting.
|
||||
*/
|
||||
void
|
||||
await_suspend(std::coroutine_handle<>) const
|
||||
{
|
||||
runner_.onSuspend();
|
||||
}
|
||||
|
||||
void
|
||||
await_resume() const noexcept
|
||||
{
|
||||
}
|
||||
};
|
||||
// NOLINTEND(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
return SuspendAwaiter{*this};
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspend and immediately repost on the JobQueue. Equivalent to
|
||||
* `co_await JobQueueAwaiter{runner}` but uses an inline struct
|
||||
* to work around a GCC-12 codegen bug (see declaration in JobQueue.h).
|
||||
*
|
||||
* If the JobQueue is stopping (post fails), the suspend count is
|
||||
* undone and the coroutine continues immediately via symmetric
|
||||
* transfer back to its own handle.
|
||||
*
|
||||
* @return An inline YieldPostAwaiter
|
||||
*/
|
||||
inline auto
|
||||
JobQueue::CoroTaskRunner::yieldAndPost()
|
||||
{
|
||||
// The C++ coroutine protocol mandates these awaiter names and
|
||||
// instance-callable methods, which conflict with the project
|
||||
// naming/static conventions.
|
||||
// NOLINTBEGIN(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
struct YieldPostAwaiter
|
||||
{
|
||||
CoroTaskRunner& runner_;
|
||||
|
||||
[[nodiscard]] bool
|
||||
await_ready() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a coroutine_handle<> (symmetric transfer) rather than
|
||||
* void + h.resume(). Two reasons:
|
||||
*
|
||||
* 1. h.resume() runs the coroutine nested inside this frame. A
|
||||
* coroutine that yields in a loop against a stopping JobQueue
|
||||
* fails post() every iteration, so the stack grows without
|
||||
* bound. Symmetric transfer is a tail call and does not nest.
|
||||
*
|
||||
* 2. After h.resume() returns, the coroutine may have completed
|
||||
* and destroyed its frame -- the frame this awaiter lives in.
|
||||
* Returning from await_suspend would then touch freed memory.
|
||||
*
|
||||
* A bool return would also avoid nesting, but GCC-12 miscompiles
|
||||
* bool-returning await_suspend (see JobQueueAwaiter.h).
|
||||
*
|
||||
* @return noop_coroutine() to stay suspended (job posted);
|
||||
* the caller's handle to continue now (JQ stopping)
|
||||
*/
|
||||
std::coroutine_handle<>
|
||||
await_suspend(std::coroutine_handle<> h)
|
||||
{
|
||||
runner_.onSuspend();
|
||||
if (!runner_.post())
|
||||
{
|
||||
runner_.onUndoSuspend();
|
||||
return h;
|
||||
}
|
||||
return std::noop_coroutine();
|
||||
}
|
||||
|
||||
void
|
||||
await_resume() const noexcept
|
||||
{
|
||||
}
|
||||
};
|
||||
// NOLINTEND(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
return YieldPostAwaiter{*this};
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule coroutine resumption as a job on the JobQueue.
|
||||
* A shared_ptr capture (sp) prevents this CoroTaskRunner from being
|
||||
* destroyed while the job is queued but not yet executed.
|
||||
*
|
||||
* @return false if the JobQueue rejected the job (shutting down)
|
||||
*/
|
||||
inline bool
|
||||
JobQueue::CoroTaskRunner::post()
|
||||
{
|
||||
{
|
||||
std::scoped_lock const lk(mutexRun_);
|
||||
++runCount_;
|
||||
}
|
||||
|
||||
// sp prevents 'this' from being destroyed while the job is pending
|
||||
if (jq_.addJob(type_, name_, [this, sp = shared_from_this()]() { resume(); }))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// The coroutine will not run. Undo the runCount_ increment.
|
||||
std::scoped_lock const lk(mutexRun_);
|
||||
--runCount_;
|
||||
cv_.notify_all();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume the coroutine on the current thread.
|
||||
*
|
||||
* Steps:
|
||||
* 1. Decrement nSuspend_ (under jq_.mutex_)
|
||||
* 2. Swap in this coroutine's LocalValues for thread-local isolation
|
||||
* 3. Resume the coroutine handle (under mutex_)
|
||||
* 4. Swap out LocalValues, restoring the thread's previous state
|
||||
* 5. Decrement runCount_ and notify join() waiters
|
||||
*
|
||||
* @pre post() must have been called before resume(). Direct calls
|
||||
* without a prior post() will corrupt runCount_ and break join().
|
||||
* Note: runCount_ is NOT incremented here — post() already did that.
|
||||
* This ensures join() stays blocked for the entire post->resume lifetime.
|
||||
*/
|
||||
inline void
|
||||
JobQueue::CoroTaskRunner::resume()
|
||||
{
|
||||
{
|
||||
std::scoped_lock const lock(jq_.mutex_);
|
||||
--jq_.nSuspend_;
|
||||
}
|
||||
auto saved = detail::getLocalValues().release();
|
||||
detail::getLocalValues().reset(&lvs_);
|
||||
std::scoped_lock const lock(mutex_);
|
||||
XRPL_ASSERT(
|
||||
task_.handle() && !task_.done(),
|
||||
"xrpl::JobQueue::CoroTaskRunner::resume : task handle is valid and not done");
|
||||
if (task_.handle() && !task_.done())
|
||||
{
|
||||
task_.handle().resume();
|
||||
}
|
||||
else
|
||||
{
|
||||
// A resume() with no coroutine to run (e.g. a duplicate external
|
||||
// post() after completion). Resuming a null or finished handle is
|
||||
// undefined behavior, so skip it -- this matches the old
|
||||
// Coro::resume() `if (coro_)` guard. The bookkeeping below still
|
||||
// runs to balance the ++runCount_ done by the post() that
|
||||
// scheduled this call.
|
||||
JLOG(jq_.journal_.warn())
|
||||
<< "CoroTaskRunner::resume called for coroutine '" << name_
|
||||
<< "' with no runnable coroutine (duplicate post or already completed)";
|
||||
}
|
||||
detail::getLocalValues().release();
|
||||
detail::getLocalValues().reset(saved);
|
||||
if (task_.done())
|
||||
{
|
||||
finished_ = true;
|
||||
// An exception that escapes a top-level coroutine body is captured
|
||||
// by promise_type::unhandled_exception() but has no awaiter to
|
||||
// rethrow it, so it would vanish with the frame. Surface it in the
|
||||
// log. (The old Boost path propagated it out of resume() instead.)
|
||||
if (auto const& ep = task_.handle().promise().exception_)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::rethrow_exception(ep);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
JLOG(jq_.journal_.error())
|
||||
<< "Unhandled exception in coroutine '" << name_ << "': " << e.what();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
JLOG(jq_.journal_.error())
|
||||
<< "Unhandled non-standard exception in coroutine '" << name_ << "'";
|
||||
}
|
||||
}
|
||||
// Break the shared_ptr cycle: frame -> shared_ptr<runner> -> this.
|
||||
// Use std::move (not task_ = {}) so task_.handle_ is null BEFORE the
|
||||
// frame is destroyed. operator= would destroy the frame while handle_
|
||||
// still holds the old value -- a re-entrancy hazard on GCC-12 if
|
||||
// frame destruction triggers runner cleanup.
|
||||
[[maybe_unused]] auto completed = std::move(task_);
|
||||
}
|
||||
std::scoped_lock const lk(mutexRun_);
|
||||
--runCount_;
|
||||
cv_.notify_all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the coroutine has not yet run to completion
|
||||
*/
|
||||
inline bool
|
||||
JobQueue::CoroTaskRunner::runnable() const
|
||||
{
|
||||
// After normal completion, task_ is reset to break the shared_ptr cycle
|
||||
// (handle_ becomes null). A null handle means the coroutine is done.
|
||||
return task_.handle() && !task_.done();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle early termination when the coroutine never ran (e.g. JobQueue
|
||||
* is stopping). Decrements nSuspend_ and destroys the coroutine frame
|
||||
* to break the shared_ptr cycle: frame -> lambda -> runner -> frame.
|
||||
*/
|
||||
inline void
|
||||
JobQueue::CoroTaskRunner::expectEarlyExit()
|
||||
{
|
||||
if (!finished_)
|
||||
{
|
||||
std::scoped_lock const lock(jq_.mutex_);
|
||||
--jq_.nSuspend_;
|
||||
finished_ = true;
|
||||
}
|
||||
// Break the shared_ptr cycle: frame -> shared_ptr<runner> -> this.
|
||||
// The coroutine is at initial_suspend and never ran user code, so
|
||||
// destroying it is safe. Use std::move (not task_ = {}) so
|
||||
// task_.handle_ is null before the frame is destroyed.
|
||||
{
|
||||
[[maybe_unused]] auto completed = std::move(task_);
|
||||
}
|
||||
storedFunc_.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Block until all pending/active resume operations complete.
|
||||
* Uses cv_ + mutexRun_ to wait until runCount_ reaches 0 or
|
||||
* finished_ becomes true. The finished_ check handles the case
|
||||
* where resume() is called directly (without post()), which
|
||||
* decrements runCount_ below zero. In that scenario runCount_
|
||||
* never returns to 0, but finished_ becoming true guarantees
|
||||
* the coroutine is done and no more resumes will occur.
|
||||
*
|
||||
* Note: when join() returns via the finished_ disjunct, the final
|
||||
* resume() call may still be executing its post-completion
|
||||
* bookkeeping (the --runCount_ / notify after finished_ is set).
|
||||
* That is safe -- the coroutine body has fully completed and the
|
||||
* runner is kept alive by the resume job's shared_ptr -- but
|
||||
* callers must not assume resume() itself has returned.
|
||||
*/
|
||||
inline void
|
||||
JobQueue::CoroTaskRunner::join()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutexRun_);
|
||||
cv_.wait(lk, [this]() { return runCount_ == 0 || finished_; });
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -88,7 +88,7 @@ class Config;
|
||||
class HashRouter
|
||||
{
|
||||
public:
|
||||
// The type here *MUST* match the type of Peer::id_t
|
||||
// The type here *MUST* match the type of Peer::ID
|
||||
using PeerShortID = std::uint32_t;
|
||||
|
||||
/**
|
||||
@@ -107,17 +107,17 @@ public:
|
||||
*/
|
||||
explicit Setup() = default;
|
||||
|
||||
using seconds = std::chrono::seconds;
|
||||
using Seconds = std::chrono::seconds;
|
||||
|
||||
/**
|
||||
* Expiration time for a hash entry
|
||||
*/
|
||||
seconds holdTime{300};
|
||||
Seconds holdTime{300};
|
||||
|
||||
/**
|
||||
* Amount of time required before a relayed item will be relayed again.
|
||||
*/
|
||||
seconds relayTime{30};
|
||||
Seconds relayTime{30};
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -213,10 +213,10 @@ public:
|
||||
// VFALCO TODO Replace "Suppression" terminology with something more
|
||||
// semantically meaningful.
|
||||
void
|
||||
addSuppression(uint256 const& key);
|
||||
addSuppression(UInt256 const& key);
|
||||
|
||||
bool
|
||||
addSuppressionPeer(uint256 const& key, PeerShortID peer);
|
||||
addSuppressionPeer(UInt256 const& key, PeerShortID peer);
|
||||
|
||||
/**
|
||||
* Add a suppression peer and get message's relay status.
|
||||
@@ -226,15 +226,15 @@ public:
|
||||
* is unseated if has not relayed yet.
|
||||
*/
|
||||
std::pair<bool, std::optional<Stopwatch::time_point>>
|
||||
addSuppressionPeerWithStatus(uint256 const& key, PeerShortID peer);
|
||||
addSuppressionPeerWithStatus(UInt256 const& key, PeerShortID peer);
|
||||
|
||||
bool
|
||||
addSuppressionPeer(uint256 const& key, PeerShortID peer, HashRouterFlags& flags);
|
||||
addSuppressionPeer(UInt256 const& key, PeerShortID peer, HashRouterFlags& flags);
|
||||
|
||||
// Add a peer suppression and return whether the entry should be processed
|
||||
bool
|
||||
shouldProcess(
|
||||
uint256 const& key,
|
||||
UInt256 const& key,
|
||||
PeerShortID peer,
|
||||
HashRouterFlags& flags,
|
||||
std::chrono::seconds txInterval);
|
||||
@@ -245,10 +245,10 @@ public:
|
||||
* @return `true` if the flags were changed. `false` if unchanged.
|
||||
*/
|
||||
bool
|
||||
setFlags(uint256 const& key, HashRouterFlags flags);
|
||||
setFlags(UInt256 const& key, HashRouterFlags flags);
|
||||
|
||||
HashRouterFlags
|
||||
getFlags(uint256 const& key);
|
||||
getFlags(UInt256 const& key);
|
||||
|
||||
/**
|
||||
* Determines whether the hashed item should be relayed.
|
||||
@@ -264,12 +264,12 @@ public:
|
||||
* _not_ be relayed.
|
||||
*/
|
||||
std::optional<std::set<PeerShortID>>
|
||||
shouldRelay(uint256 const& key);
|
||||
shouldRelay(UInt256 const& key);
|
||||
|
||||
private:
|
||||
// pair.second indicates whether the entry was created
|
||||
std::pair<Entry&, bool>
|
||||
emplace(uint256 const&);
|
||||
emplace(UInt256 const&);
|
||||
|
||||
std::mutex mutable mutex_;
|
||||
|
||||
@@ -277,7 +277,7 @@ private:
|
||||
Setup const setup_;
|
||||
|
||||
// Stores all suppressed hashes and their expiration time
|
||||
beast::aged_unordered_map<uint256, Entry, Stopwatch::clock_type, HardenedHash<strong_hash>>
|
||||
beast::AgedUnorderedMap<UInt256, Entry, Stopwatch::ClockType, HardenedHash<StrongHash>>
|
||||
suppressionMap_;
|
||||
};
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ enum JobType {
|
||||
class Job : public CountedObject<Job>
|
||||
{
|
||||
public:
|
||||
using clock_type = std::chrono::steady_clock;
|
||||
using ClockType = std::chrono::steady_clock;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
/**
|
||||
* Returns the time when the job was queued.
|
||||
*/
|
||||
[[nodiscard]] clock_type::time_point const&
|
||||
[[nodiscard]] ClockType::time_point const&
|
||||
queueTime() const;
|
||||
|
||||
void
|
||||
@@ -135,7 +135,7 @@ private:
|
||||
std::function<void()> job_;
|
||||
std::shared_ptr<LoadEvent> loadEvent_;
|
||||
std::string name_;
|
||||
clock_type::time_point queueTime_;
|
||||
ClockType::time_point queueTime_;
|
||||
};
|
||||
|
||||
using JobCounter = ClosureCounter<void>;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/LocalValue.h>
|
||||
#include <xrpl/core/ClosureCounter.h>
|
||||
#include <xrpl/core/CoroTask.h>
|
||||
#include <xrpl/core/JobTypeData.h>
|
||||
#include <xrpl/core/detail/Workers.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
@@ -152,11 +153,425 @@ public:
|
||||
join();
|
||||
};
|
||||
|
||||
/**
|
||||
* C++20 coroutine lifecycle manager. Replaces Coro for new code.
|
||||
*
|
||||
* Class / Inheritance / Dependency Diagram
|
||||
* =========================================
|
||||
*
|
||||
* std::enable_shared_from_this<CoroTaskRunner>
|
||||
* ^
|
||||
* | (public inheritance)
|
||||
* |
|
||||
* CoroTaskRunner
|
||||
* +---------------------------------------------------+
|
||||
* | - lvs_ : detail::LocalValues |
|
||||
* | - jq_ : JobQueue& |
|
||||
* | - type_ : JobType |
|
||||
* | - name_ : std::string |
|
||||
* | - runCount_ : int (in-flight resumes) |
|
||||
* | - mutex_ : std::mutex (coroutine guard) |
|
||||
* | - mutexRun_ : std::mutex (join guard) |
|
||||
* | - cv_ : condition_variable |
|
||||
* | - task_ : CoroTask<void> |
|
||||
* | - storedFunc_ : unique_ptr<FuncBase> (type-erased)|
|
||||
* +---------------------------------------------------+
|
||||
* | + init(F&&) : set up coroutine callable |
|
||||
* | + onSuspend() : ++jq_.nSuspend_ |
|
||||
* | + onUndoSuspend() : --jq_.nSuspend_ |
|
||||
* | + suspend() : returns SuspendAwaiter |
|
||||
* | + post() : schedule resume on JobQueue |
|
||||
* | + resume() : resume coroutine on caller |
|
||||
* | + runnable() : !task_.done() |
|
||||
* | + expectEarlyExit() : teardown for failed post |
|
||||
* | + join() : block until not running |
|
||||
* +---------------------------------------------------+
|
||||
* | |
|
||||
* | owns | references
|
||||
* v v
|
||||
* CoroTask<void> JobQueue
|
||||
* (coroutine frame) (thread pool + nSuspend_)
|
||||
*
|
||||
* FuncBase / FuncStore<F> (type-erased heap storage
|
||||
* for the coroutine lambda)
|
||||
*
|
||||
* Coroutine Lifecycle (Control Flow)
|
||||
* ===================================
|
||||
*
|
||||
* Caller thread JobQueue worker thread
|
||||
* ------------- ----------------------
|
||||
* postCoroTask(f)
|
||||
* |
|
||||
* +-- reserve a jobCounter_ slot (reject if JQ shutting down)
|
||||
* +-- ++nSuspend_ (lazy start counts as suspended)
|
||||
* +-- make_shared<CoroTaskRunner>
|
||||
* +-- init(f)
|
||||
* | +-- store lambda on heap (FuncStore)
|
||||
* | +-- task_ = f(shared_from_this())
|
||||
* | [coroutine created, suspended at initial_suspend]
|
||||
* +-- post()
|
||||
* | +-- ++runCount_
|
||||
* | +-- addJob(type_, [resume]{})
|
||||
* | resume()
|
||||
* | |
|
||||
* | +-- --nSuspend_
|
||||
* | +-- swap in LocalValues
|
||||
* | +-- task_.handle().resume()
|
||||
* | | [coroutine body runs]
|
||||
* | | ...
|
||||
* | | co_await suspend()
|
||||
* | | +-- ++nSuspend_
|
||||
* | | [coroutine suspends]
|
||||
* | +-- swap out LocalValues
|
||||
* | +-- --runCount_
|
||||
* | +-- cv_.notify_all()
|
||||
* |
|
||||
* post() <-- called externally or by yieldAndPost()
|
||||
* +-- ++runCount_
|
||||
* +-- addJob(type_, [resume]{})
|
||||
* resume()
|
||||
* |
|
||||
* +-- [coroutine body continues]
|
||||
* +-- co_return
|
||||
* +-- --runCount_
|
||||
* +-- cv_.notify_all()
|
||||
* join()
|
||||
* +-- cv_.wait([]{runCount_ == 0})
|
||||
* +-- [done]
|
||||
*
|
||||
* Usage Examples
|
||||
* ==============
|
||||
*
|
||||
* 1. Fire-and-forget coroutine (most common pattern):
|
||||
*
|
||||
* jq.postCoroTask(JtClient, "MyWork",
|
||||
* [](auto runner) -> CoroTask<void> {
|
||||
* doSomeWork();
|
||||
* co_await runner->suspend(); // yield to other jobs
|
||||
* doMoreWork();
|
||||
* co_return;
|
||||
* });
|
||||
*
|
||||
* 2. Manually controlling suspend / resume (external trigger):
|
||||
*
|
||||
* auto runner = jq.postCoroTask(JtClient, "ExtTrigger",
|
||||
* [&result](auto runner) -> CoroTask<void> {
|
||||
* startAsyncOperation(callback);
|
||||
* co_await runner->suspend();
|
||||
* // callback called runner->post() to get here
|
||||
* result = collectResult();
|
||||
* co_return;
|
||||
* });
|
||||
* // ... later, from the callback:
|
||||
* runner->post(); // reschedule the coroutine on the JobQueue
|
||||
*
|
||||
* 3. Using yieldAndPost() for automatic suspend + repost:
|
||||
*
|
||||
* jq.postCoroTask(JtClient, "AutoRepost",
|
||||
* [](auto runner) -> CoroTask<void> {
|
||||
* step1();
|
||||
* co_await runner->yieldAndPost(); // yield + auto-repost
|
||||
* step2();
|
||||
* co_await runner->yieldAndPost();
|
||||
* step3();
|
||||
* co_return;
|
||||
* });
|
||||
*
|
||||
* 4. Checking shutdown after co_await (cooperative cancellation):
|
||||
*
|
||||
* jq.postCoroTask(JtClient, "Cancellable",
|
||||
* [&jq](auto runner) -> CoroTask<void> {
|
||||
* while (moreWork()) {
|
||||
* co_await runner->yieldAndPost();
|
||||
* if (jq.isStopping())
|
||||
* co_return; // bail out cleanly
|
||||
* processNextItem();
|
||||
* }
|
||||
* co_return;
|
||||
* });
|
||||
*
|
||||
* Caveats / Pitfalls
|
||||
* ==================
|
||||
*
|
||||
* BUG-RISK: Calling suspend() without a matching post()/resume().
|
||||
* After co_await runner->suspend(), the coroutine is parked and
|
||||
* nSuspend_ is incremented. If nothing ever calls post() or
|
||||
* resume(), the coroutine is leaked and JobQueue::stop() will
|
||||
* hang forever waiting for nSuspend_ to reach zero.
|
||||
*
|
||||
* BUG-RISK: Calling post() on an already-running coroutine.
|
||||
* post() schedules a resume() job. If the coroutine has not
|
||||
* actually suspended yet (no co_await executed), the resume job
|
||||
* will try to call handle().resume() while the coroutine is still
|
||||
* running on another thread. This is UB. The mutex_ prevents
|
||||
* data corruption but the logic is wrong — always co_await
|
||||
* suspend() before calling post(). (The test incorrect_order()
|
||||
* shows this works only because mutex_ serializes the calls.)
|
||||
*
|
||||
* BUG-RISK: Dropping the shared_ptr<CoroTaskRunner> before join().
|
||||
* The CoroTaskRunner destructor asserts that finished_ is true
|
||||
* (the coroutine completed). If you let the last shared_ptr die
|
||||
* while the coroutine is still running or suspended, you get an
|
||||
* assertion failure in debug and UB in release. Always call
|
||||
* join() or expectEarlyExit() first.
|
||||
*
|
||||
* BUG-RISK: Lambda captures outliving the coroutine frame.
|
||||
* The lambda passed to postCoroTask is heap-allocated (FuncStore)
|
||||
* to prevent dangling. But objects captured by pointer still need
|
||||
* their own lifetime management. If you capture a raw pointer to
|
||||
* a stack variable, and the stack frame exits before the coroutine
|
||||
* finishes, the pointer dangles. Use shared_ptr or ensure the
|
||||
* pointed-to object outlives the coroutine.
|
||||
*
|
||||
* BUG-RISK: Forgetting co_return in a void coroutine.
|
||||
* If the coroutine body falls off the end without co_return,
|
||||
* the compiler may silently treat it as co_return (per standard),
|
||||
* but some compilers warn. Always write explicit co_return.
|
||||
*
|
||||
* LIMITATION: CoroTaskRunner only supports CoroTask<void>.
|
||||
* The task_ member is CoroTask<void>. To return values from
|
||||
* the top-level coroutine, write through a captured pointer
|
||||
* (as the tests demonstrate), or co_await inner CoroTask<T>
|
||||
* coroutines that return values.
|
||||
*
|
||||
* LIMITATION: One coroutine per CoroTaskRunner.
|
||||
* init() must be called exactly once. You cannot reuse a
|
||||
* CoroTaskRunner to run a second coroutine. Create a new one
|
||||
* via postCoroTask() instead.
|
||||
*
|
||||
* LIMITATION: No timeout on join().
|
||||
* join() blocks indefinitely. If the coroutine is suspended
|
||||
* and never posted, join() will deadlock. Use timed waits
|
||||
* on the gate pattern (condition_variable + wait_for) in tests.
|
||||
*/
|
||||
class CoroTaskRunner : public std::enable_shared_from_this<CoroTaskRunner>
|
||||
{
|
||||
private:
|
||||
// Per-coroutine thread-local storage. Swapped in before resume()
|
||||
// and swapped out after, so each coroutine sees its own LocalValue
|
||||
// state regardless of which worker thread executes it.
|
||||
detail::LocalValues lvs_;
|
||||
|
||||
// Back-reference to the owning JobQueue. Used to post jobs,
|
||||
// increment/decrement nSuspend_, and acquire jq_.mutex_.
|
||||
JobQueue& jq_;
|
||||
|
||||
// Job type passed to addJob() when posting this coroutine.
|
||||
JobType type_;
|
||||
|
||||
// Human-readable name for this coroutine job (for logging).
|
||||
std::string name_;
|
||||
|
||||
// Number of in-flight resume operations (pending + active).
|
||||
// Incremented by post(), decremented when resume() finishes.
|
||||
// Guarded by mutexRun_. join() blocks until this reaches 0.
|
||||
//
|
||||
// A counter (not a bool) is needed because post() can be called
|
||||
// from within the coroutine body (e.g. via yieldAndPost()),
|
||||
// enqueuing a second resume while the first is still running.
|
||||
// A bool would be clobbered: R2.post() sets true, then R1's
|
||||
// cleanup sets false — losing the fact that R2 is still pending.
|
||||
int runCount_ = 0;
|
||||
|
||||
// Serializes all coroutine resume() calls, preventing concurrent
|
||||
// execution of the coroutine body on multiple threads. Handles the
|
||||
// race where post() enqueues a resume before the coroutine has
|
||||
// actually suspended (post-before-suspend pattern).
|
||||
std::mutex mutex_;
|
||||
|
||||
// Guards runCount_. Used with cv_ for join() to wait
|
||||
// until all pending/active resume operations complete.
|
||||
std::mutex mutexRun_;
|
||||
|
||||
// Notified when runCount_ reaches zero, allowing
|
||||
// join() waiters to wake up.
|
||||
std::condition_variable cv_;
|
||||
|
||||
// The coroutine handle wrapper. Owns the coroutine frame.
|
||||
// Set by init(). Reset to empty in resume() upon coroutine
|
||||
// completion (to break the shared_ptr cycle) or in
|
||||
// expectEarlyExit() on early termination.
|
||||
CoroTask<void> task_;
|
||||
|
||||
/**
|
||||
* Type-erased base for heap-stored callables.
|
||||
* Prevents the coroutine lambda from being destroyed before
|
||||
* the coroutine frame is done with it.
|
||||
*
|
||||
* @see FuncStore
|
||||
*/
|
||||
struct FuncBase
|
||||
{
|
||||
virtual ~FuncBase() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Concrete type-erased storage for a callable of type F.
|
||||
* The coroutine frame stores a reference to the lambda's implicit
|
||||
* object parameter. If the lambda is a temporary, that reference
|
||||
* dangles after the call returns. FuncStore keeps it alive on
|
||||
* the heap for the lifetime of the CoroTaskRunner.
|
||||
*/
|
||||
template <class F>
|
||||
struct FuncStore : FuncBase
|
||||
{
|
||||
F func; // The stored callable (coroutine lambda).
|
||||
explicit FuncStore(F&& f) : func(std::move(f))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Heap-allocated callable storage. Set by init(), ensures the
|
||||
// lambda outlives the coroutine frame that references it.
|
||||
std::unique_ptr<FuncBase> storedFunc_;
|
||||
|
||||
// True once the coroutine has completed or expectEarlyExit() was
|
||||
// called. Asserted in the destructor (debug) to catch leaked
|
||||
// runners. Available in all builds to guard expectEarlyExit()
|
||||
// against double-decrementing nSuspend_.
|
||||
bool finished_ = false;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Tag type for private construction. Prevents external code
|
||||
* from constructing CoroTaskRunner directly. Use postCoroTask().
|
||||
*/
|
||||
struct CreateT
|
||||
{
|
||||
explicit CreateT() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a CoroTaskRunner. Private by convention (CreateT tag).
|
||||
*
|
||||
* @param jq The JobQueue this coroutine will run on
|
||||
* @param type Job type for scheduling priority
|
||||
* @param name Human-readable name for logging
|
||||
*/
|
||||
CoroTaskRunner(CreateT, JobQueue&, JobType, std::string);
|
||||
|
||||
CoroTaskRunner(CoroTaskRunner const&) = delete;
|
||||
CoroTaskRunner&
|
||||
operator=(CoroTaskRunner const&) = delete;
|
||||
|
||||
/**
|
||||
* Destructor. Asserts (debug) that the coroutine has finished
|
||||
* or expectEarlyExit() was called.
|
||||
*/
|
||||
~CoroTaskRunner();
|
||||
|
||||
/**
|
||||
* Initialize with a coroutine-returning callable.
|
||||
* Must be called exactly once, after the object is managed by
|
||||
* shared_ptr (because init uses shared_from_this internally).
|
||||
* This is handled automatically by postCoroTask().
|
||||
*
|
||||
* @param f Callable: CoroTask<void>(shared_ptr<CoroTaskRunner>)
|
||||
*/
|
||||
template <class F>
|
||||
void
|
||||
init(F&& f);
|
||||
|
||||
/**
|
||||
* Increment the JobQueue's suspended-coroutine count (nSuspend_).
|
||||
* Called when the coroutine is about to suspend. Every call
|
||||
* must be balanced by a corresponding decrement (via resume()
|
||||
* or onUndoSuspend()), or JobQueue::stop() will hang.
|
||||
*/
|
||||
void
|
||||
onSuspend();
|
||||
|
||||
/**
|
||||
* Decrement nSuspend_ without resuming.
|
||||
* Used to undo onSuspend() when a scheduled post() fails
|
||||
* (e.g. JobQueue is stopping).
|
||||
*/
|
||||
void
|
||||
onUndoSuspend();
|
||||
|
||||
/**
|
||||
* Suspend the coroutine.
|
||||
* The awaiter's await_suspend() increments nSuspend_ before the
|
||||
* coroutine actually suspends. The caller must later call post()
|
||||
* or resume() to continue execution.
|
||||
*
|
||||
* @return An awaiter for use with `co_await runner->suspend()`
|
||||
*/
|
||||
auto
|
||||
suspend();
|
||||
|
||||
/**
|
||||
* Suspend the coroutine and immediately repost it on the
|
||||
* JobQueue. Combines suspend() + post() atomically inside
|
||||
* await_suspend, so there is no window where an external
|
||||
* event could race between the two.
|
||||
*
|
||||
* Equivalent to JobQueueAwaiter but defined as an inline
|
||||
* awaiter returned from a member function. This avoids a
|
||||
* GCC-12 coroutine codegen bug where an external awaiter
|
||||
* struct (JobQueueAwaiter) used at multiple co_await points
|
||||
* corrupts the coroutine state machine's resume index,
|
||||
* causing the coroutine to hang on the third resumption.
|
||||
*
|
||||
* @return An awaiter for use with `co_await runner->yieldAndPost()`
|
||||
*/
|
||||
auto
|
||||
yieldAndPost();
|
||||
|
||||
/**
|
||||
* Schedule coroutine resumption as a job on the JobQueue.
|
||||
* Captures shared_from_this() to prevent this runner from being
|
||||
* destroyed while the job is queued.
|
||||
*
|
||||
* @return true if the job was accepted; false if the JobQueue
|
||||
* is stopping (caller must handle cleanup)
|
||||
*/
|
||||
bool
|
||||
post();
|
||||
|
||||
/**
|
||||
* Resume the coroutine on the current thread.
|
||||
* Decrements nSuspend_, swaps in LocalValues, resumes the
|
||||
* coroutine handle, swaps out LocalValues, and notifies join()
|
||||
* waiters. Lock ordering (sequential, non-overlapping):
|
||||
* jq_.mutex_ -> mutex_ -> mutexRun_.
|
||||
*
|
||||
* @pre post() must have been called before resume(). Direct
|
||||
* calls without a prior post() will corrupt runCount_
|
||||
* and break join().
|
||||
*/
|
||||
void
|
||||
resume();
|
||||
|
||||
/**
|
||||
* @return true if the coroutine has not yet run to completion
|
||||
*/
|
||||
bool
|
||||
runnable() const;
|
||||
|
||||
/**
|
||||
* Handle early termination when the coroutine never ran.
|
||||
* Decrements nSuspend_ and destroys the coroutine frame to
|
||||
* break the shared_ptr cycle (frame -> lambda -> runner -> frame).
|
||||
* Called by postCoroTask() when post() fails.
|
||||
*/
|
||||
void
|
||||
expectEarlyExit();
|
||||
|
||||
/**
|
||||
* Block until all pending/active resume operations complete.
|
||||
* Uses cv_ + mutexRun_ to wait until runCount_ reaches 0.
|
||||
* Warning: deadlocks if the coroutine is suspended and never posted.
|
||||
*/
|
||||
void
|
||||
join();
|
||||
};
|
||||
|
||||
using JobFunction = std::function<void()>;
|
||||
|
||||
JobQueue(
|
||||
int threadCount,
|
||||
beast::insight::Collector::ptr const& collector,
|
||||
beast::insight::Collector::Ptr const& collector,
|
||||
beast::Journal journal,
|
||||
Logs& logs,
|
||||
perf::PerfLog& perfLog);
|
||||
@@ -197,6 +612,20 @@ public:
|
||||
std::shared_ptr<Coro>
|
||||
postCoro(JobType t, std::string const& name, F&& f);
|
||||
|
||||
/**
|
||||
* Creates a C++20 coroutine and adds a job to the queue to run it.
|
||||
*
|
||||
* @param t The type of job.
|
||||
* @param name Name of the job.
|
||||
* @param f Callable with signature
|
||||
* CoroTask<void>(std::shared_ptr<CoroTaskRunner>).
|
||||
*
|
||||
* @return shared_ptr to posted CoroTaskRunner. nullptr if not successful.
|
||||
*/
|
||||
template <class F>
|
||||
std::shared_ptr<CoroTaskRunner>
|
||||
postCoroTask(JobType t, std::string const& name, F&& f);
|
||||
|
||||
/**
|
||||
* Jobs waiting at this priority.
|
||||
*/
|
||||
@@ -280,7 +709,7 @@ private:
|
||||
|
||||
// Statistics tracking
|
||||
perf::PerfLog& perfLog_;
|
||||
beast::insight::Collector::ptr collector_;
|
||||
beast::insight::Collector::Ptr collector_;
|
||||
beast::insight::Gauge jobCount_;
|
||||
beast::insight::Hook hook_;
|
||||
|
||||
@@ -419,7 +848,8 @@ private:
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#include <xrpl/core/Coro.ipp> // IWYU pragma: keep
|
||||
#include <xrpl/core/Coro.ipp> // IWYU pragma: keep
|
||||
#include <xrpl/core/CoroTaskRunner.ipp> // IWYU pragma: keep
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -442,4 +872,82 @@ JobQueue::postCoro(JobType t, std::string const& name, F&& f)
|
||||
return coro;
|
||||
}
|
||||
|
||||
// postCoroTask — entry point for launching a C++20 coroutine on the JobQueue.
|
||||
//
|
||||
// Control Flow
|
||||
// ============
|
||||
//
|
||||
// postCoroTask(t, name, f)
|
||||
// |
|
||||
// +-- 1. Reserve a jobCounter_ slot — reject if JQ shutting down
|
||||
// |
|
||||
// +-- 2. ++nSuspend_ (mirrors Boost Coro ctor's implicit yield)
|
||||
// | The coroutine is "suspended" from the JobQueue's perspective
|
||||
// | even though it hasn't run yet — this keeps the JQ shutdown
|
||||
// | logic correct (it waits for nSuspend_ to reach 0).
|
||||
// |
|
||||
// +-- 3. Create CoroTaskRunner (shared_ptr, ref-counted)
|
||||
// |
|
||||
// +-- 4. runner->init(f)
|
||||
// | +-- Heap-allocate the lambda (FuncStore) to prevent
|
||||
// | | dangling captures in the coroutine frame
|
||||
// | +-- task_ = f(shared_from_this())
|
||||
// | [coroutine created but NOT started — lazy initial_suspend]
|
||||
// |
|
||||
// +-- 5. runner->post()
|
||||
// | +-- addJob(type_, [resume]{}) → resume on worker thread
|
||||
// | +-- failure (JQ stopping):
|
||||
// | +-- runner->expectEarlyExit()
|
||||
// | | --nSuspend_, destroy coroutine frame
|
||||
// | +-- return nullptr
|
||||
// |
|
||||
// +-- 6. Release the jobCounter_ slot (on return)
|
||||
//
|
||||
// Why reserve a jobCounter_ slot?
|
||||
// ===============================
|
||||
// JobQueue::stop() joins jobCounter_ before it acquires mutex_ and asserts
|
||||
// nSuspend_ == 0. Without a reservation, stop() could run to completion in
|
||||
// the window between the ++nSuspend_ in step 2 and the balancing post() or
|
||||
// expectEarlyExit() in step 5, tripping that assert. Holding a slot blocks
|
||||
// stop()'s join() for the whole function, closing the window. wrap() also
|
||||
// returns nullopt once the counter is joined, so it doubles as the shutdown
|
||||
// check — a plain stopping_ read cannot do this, because the read and the
|
||||
// ++nSuspend_ are not a single atomic step.
|
||||
//
|
||||
// Why async post() instead of synchronous resume()?
|
||||
// ==================================================
|
||||
// The initial dispatch MUST use async post() so the coroutine body runs on
|
||||
// a JobQueue worker thread, not the caller's thread. resume() swaps the
|
||||
// caller's thread-local LocalValues with the coroutine's private copy.
|
||||
// If the coroutine mutates LocalValues (e.g. thread_specific_storage test),
|
||||
// those mutations bleed back into the caller's thread-local state after the
|
||||
// swap-out, corrupting subsequent tests that share the same thread pool.
|
||||
// Async post() avoids this by running the coroutine on a worker thread whose
|
||||
// LocalValues are managed by the thread pool, not by the caller.
|
||||
//
|
||||
template <class F>
|
||||
std::shared_ptr<JobQueue::CoroTaskRunner>
|
||||
JobQueue::postCoroTask(JobType t, std::string const& name, F&& f)
|
||||
{
|
||||
// Held until this function returns. Null once jobCounter_ is joined,
|
||||
// which is how a shutting-down JobQueue rejects new coroutines.
|
||||
auto const shutdownGuard = jobCounter_.wrap([]() {});
|
||||
if (!shutdownGuard)
|
||||
return nullptr;
|
||||
|
||||
{
|
||||
std::scoped_lock const lock(mutex_);
|
||||
++nSuspend_;
|
||||
}
|
||||
|
||||
auto runner = std::make_shared<CoroTaskRunner>(CoroTaskRunner::CreateT{}, *this, t, name);
|
||||
runner->init(std::forward<F>(f));
|
||||
if (!runner->post())
|
||||
{
|
||||
runner->expectEarlyExit();
|
||||
runner.reset();
|
||||
}
|
||||
return runner;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/JobQueue.h>
|
||||
|
||||
#include <coroutine>
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Awaiter that suspends and immediately reschedules on the JobQueue.
|
||||
* Equivalent to calling yield() followed by post() in the old Coro API.
|
||||
*
|
||||
* Usage:
|
||||
* co_await JobQueueAwaiter{runner};
|
||||
*
|
||||
* What it waits for: The coroutine is re-queued as a job and resumes
|
||||
* when a worker thread picks it up.
|
||||
*
|
||||
* Which thread resumes: A JobQueue worker thread.
|
||||
*
|
||||
* What await_resume() returns: void.
|
||||
*
|
||||
* Dependency Diagram
|
||||
* ==================
|
||||
*
|
||||
* JobQueueAwaiter
|
||||
* +----------------------------------------------+
|
||||
* | + runner : shared_ptr<CoroTaskRunner> |
|
||||
* +----------------------------------------------+
|
||||
* | + await_ready() -> false (always suspend) |
|
||||
* | + await_suspend() -> bool (suspend or cancel) |
|
||||
* | + await_resume() -> void |
|
||||
* +----------------------------------------------+
|
||||
* | |
|
||||
* | uses | uses
|
||||
* v v
|
||||
* CoroTaskRunner JobQueue
|
||||
* .onSuspend() (via runner->post() -> addJob)
|
||||
* .onUndoSuspend()
|
||||
* .post()
|
||||
*
|
||||
* Control Flow (await_suspend)
|
||||
* ============================
|
||||
*
|
||||
* co_await JobQueueAwaiter{runner}
|
||||
* |
|
||||
* +-- await_ready() -> false
|
||||
* +-- await_suspend(handle)
|
||||
* |
|
||||
* +-- runner->onSuspend() // ++nSuspend_
|
||||
* +-- runner->post() // addJob to JobQueue
|
||||
* | |
|
||||
* | +-- success? return noop_coroutine()
|
||||
* | | // coroutine stays suspended;
|
||||
* | | // worker thread will call resume()
|
||||
* | +-- failure? (JQ stopping)
|
||||
* | +-- runner->onUndoSuspend() // --nSuspend_
|
||||
* | +-- return handle // symmetric transfer back
|
||||
* | // coroutine continues immediately
|
||||
* | // so it can clean up and co_return
|
||||
*
|
||||
* DEPRECATED — prefer `co_await runner->yieldAndPost()`
|
||||
* =====================================================
|
||||
*
|
||||
* GCC-12 has a coroutine codegen bug where using this external awaiter
|
||||
* struct at multiple co_await points in the same coroutine corrupts the
|
||||
* state machine's resume index. After the second co_await, the third
|
||||
* resumption enters handle().resume() but never reaches await_resume()
|
||||
* or any subsequent user code — the coroutine hangs indefinitely.
|
||||
*
|
||||
* The fix is `co_await runner->yieldAndPost()`, which defines the
|
||||
* awaiter as an inline struct inside a CoroTaskRunner member function.
|
||||
* GCC-12 handles inline awaiters correctly at multiple co_await points.
|
||||
*
|
||||
* This struct is retained for single-use scenarios and documentation
|
||||
* purposes. For any code that may use co_await in a loop or at
|
||||
* multiple points, always use `runner->yieldAndPost()`.
|
||||
*
|
||||
* Usage Examples
|
||||
* ==============
|
||||
*
|
||||
* 1. Yield and auto-repost (preferred — works on all compilers):
|
||||
*
|
||||
* CoroTask<void> handler(auto runner) {
|
||||
* doPartA();
|
||||
* co_await runner->yieldAndPost(); // yield + repost
|
||||
* doPartB(); // runs on a worker thread
|
||||
* co_return;
|
||||
* }
|
||||
*
|
||||
* 2. Multiple yield points in a loop:
|
||||
*
|
||||
* CoroTask<void> batchProcessor(auto runner) {
|
||||
* for (auto& item : items) {
|
||||
* process(item);
|
||||
* co_await runner->yieldAndPost(); // let other jobs run
|
||||
* }
|
||||
* co_return;
|
||||
* }
|
||||
*
|
||||
* 3. Graceful shutdown — checking after resume:
|
||||
*
|
||||
* CoroTask<void> longTask(auto runner, JobQueue& jq) {
|
||||
* while (hasWork()) {
|
||||
* co_await runner->yieldAndPost();
|
||||
* // If JQ is stopping, await_suspend resumes the coroutine
|
||||
* // immediately without re-queuing. Always check
|
||||
* // isStopping() to decide whether to proceed:
|
||||
* if (jq.isStopping())
|
||||
* co_return;
|
||||
* doNextChunk();
|
||||
* }
|
||||
* co_return;
|
||||
* }
|
||||
*
|
||||
* Caveats / Pitfalls
|
||||
* ==================
|
||||
*
|
||||
* BUG-RISK: Using a stale or null runner.
|
||||
* The runner shared_ptr must be valid and point to the CoroTaskRunner
|
||||
* that owns the coroutine currently executing. Passing a runner from
|
||||
* a different coroutine, or a default-constructed shared_ptr, is UB.
|
||||
*
|
||||
* BUG-RISK: Assuming resume happens on the same thread.
|
||||
* After co_await, the coroutine resumes on whatever worker thread
|
||||
* picks up the job. Do not rely on thread-local state unless it is
|
||||
* managed through LocalValue (which CoroTaskRunner automatically
|
||||
* swaps in/out).
|
||||
*
|
||||
* BUG-RISK: Ignoring the shutdown path.
|
||||
* When the JobQueue is stopping, post() fails and await_suspend()
|
||||
* resumes the coroutine immediately (symmetric transfer back to h).
|
||||
* The coroutine body continues on the same thread. If your code
|
||||
* after co_await assumes it was re-queued and is running on a worker
|
||||
* thread, that assumption breaks during shutdown. Always handle the
|
||||
* "JQ is stopping" case, either by checking jq.isStopping() or by
|
||||
* letting the coroutine fall through to co_return naturally.
|
||||
*
|
||||
* DIFFERENCE from runner->suspend() + runner->post():
|
||||
* Both JobQueueAwaiter and yieldAndPost() combine suspend + post
|
||||
* in one atomic operation. With the manual suspend()/post() pattern,
|
||||
* there is a window between the two calls where an external event
|
||||
* could race. The atomic awaiters remove that window — onSuspend()
|
||||
* and post() happen within the same await_suspend() call while the
|
||||
* coroutine is guaranteed to be suspended. Use yieldAndPost() unless
|
||||
* you need an external party to decide *when* to call post().
|
||||
*/
|
||||
struct JobQueueAwaiter
|
||||
{
|
||||
// The CoroTaskRunner that owns the currently executing coroutine.
|
||||
std::shared_ptr<JobQueue::CoroTaskRunner> runner;
|
||||
|
||||
/**
|
||||
* Always returns false so the coroutine suspends.
|
||||
*/
|
||||
// The C++ coroutine protocol mandates these awaiter names and
|
||||
// instance-callable methods, which conflict with the project
|
||||
// naming/static conventions.
|
||||
// NOLINTBEGIN(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
[[nodiscard]] bool
|
||||
await_ready() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment nSuspend (equivalent to yield()) and schedule resume
|
||||
* on the JobQueue (equivalent to post()). If the JobQueue is
|
||||
* stopping, undoes the suspend count and transfers back to the
|
||||
* coroutine so it can clean up and co_return.
|
||||
*
|
||||
* Returns a coroutine_handle<> (symmetric transfer) instead of
|
||||
* bool to work around a GCC-12 codegen bug where bool-returning
|
||||
* await_suspend leaves the coroutine in an invalid state —
|
||||
* neither properly suspended nor resumed — causing a hang.
|
||||
*
|
||||
* WARNING: GCC-12 has an additional codegen bug where using this
|
||||
* external awaiter struct at multiple co_await points in the same
|
||||
* coroutine corrupts the state machine's resume index, causing the
|
||||
* coroutine to hang on the third resumption. Prefer
|
||||
* `co_await runner->yieldAndPost()` which uses an inline awaiter
|
||||
* that GCC-12 handles correctly.
|
||||
*
|
||||
* @return noop_coroutine() to stay suspended (job posted);
|
||||
* the caller's handle to resume immediately (JQ stopping)
|
||||
*/
|
||||
std::coroutine_handle<>
|
||||
await_suspend(std::coroutine_handle<> h)
|
||||
{
|
||||
XRPL_ASSERT(runner, "xrpl::JobQueueAwaiter::await_suspend : runner is valid");
|
||||
runner->onSuspend();
|
||||
if (!runner->post())
|
||||
{
|
||||
// JobQueue is stopping. Undo the suspend count and
|
||||
// transfer back to the coroutine so it can clean up
|
||||
// and co_return.
|
||||
runner->onUndoSuspend();
|
||||
return h;
|
||||
}
|
||||
return std::noop_coroutine();
|
||||
}
|
||||
|
||||
void
|
||||
await_resume() const noexcept
|
||||
{
|
||||
}
|
||||
// NOLINTEND(readability-identifier-naming, readability-convert-member-functions-to-static)
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -17,7 +17,7 @@ private:
|
||||
LoadMonitor load_;
|
||||
|
||||
/* Support for insight */
|
||||
beast::insight::Collector::ptr collector_;
|
||||
beast::insight::Collector::Ptr collector_;
|
||||
|
||||
public:
|
||||
/* The job category which we represent */
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
|
||||
JobTypeData(
|
||||
JobTypeInfo const& info,
|
||||
beast::insight::Collector::ptr collector,
|
||||
beast::insight::Collector::Ptr collector,
|
||||
Logs& logs) noexcept
|
||||
: load_(logs.journal("LoadMonitor")), collector_(std::move(collector)), info(info)
|
||||
|
||||
|
||||
+10
-10
@@ -32,13 +32,13 @@ namespace perf {
|
||||
class PerfLog
|
||||
{
|
||||
public:
|
||||
using steady_clock = std::chrono::steady_clock;
|
||||
using system_clock = std::chrono::system_clock;
|
||||
using steady_time_point = std::chrono::time_point<steady_clock>;
|
||||
using system_time_point = std::chrono::time_point<system_clock>;
|
||||
using seconds = std::chrono::seconds;
|
||||
using milliseconds = std::chrono::milliseconds;
|
||||
using microseconds = std::chrono::microseconds;
|
||||
using SteadyClock = std::chrono::steady_clock;
|
||||
using SystemClock = std::chrono::system_clock;
|
||||
using SteadyTimePoint = std::chrono::time_point<SteadyClock>;
|
||||
using SystemTimePoint = std::chrono::time_point<SystemClock>;
|
||||
using Seconds = std::chrono::seconds;
|
||||
using Milliseconds = std::chrono::milliseconds;
|
||||
using Microseconds = std::chrono::microseconds;
|
||||
|
||||
/**
|
||||
* Configuration from [perf] section of xrpld.cfg.
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
{
|
||||
std::filesystem::path perfLog;
|
||||
// log_interval is in milliseconds to support faster testing.
|
||||
milliseconds logInterval{seconds(1)};
|
||||
Milliseconds logInterval{Seconds(1)};
|
||||
};
|
||||
|
||||
virtual ~PerfLog() = default;
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
* @param instance JobQueue worker thread instance
|
||||
*/
|
||||
virtual void
|
||||
jobStart(JobType const type, microseconds dur, steady_time_point startTime, int instance) = 0;
|
||||
jobStart(JobType const type, Microseconds dur, SteadyTimePoint startTime, int instance) = 0;
|
||||
|
||||
/**
|
||||
* Log job finishing
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
* @param instance Jobqueue worker thread instance
|
||||
*/
|
||||
virtual void
|
||||
jobFinish(JobType const type, microseconds dur, int instance) = 0;
|
||||
jobFinish(JobType const type, Microseconds dur, int instance) = 0;
|
||||
|
||||
/**
|
||||
* Render performance counters in Json
|
||||
|
||||
@@ -40,7 +40,7 @@ template <
|
||||
class TaggedCache;
|
||||
class STLedgerEntry;
|
||||
using SLE = STLedgerEntry;
|
||||
using CachedSLEs = TaggedCache<uint256, SLE const>;
|
||||
using CachedSLEs = TaggedCache<UInt256, SLE const>;
|
||||
|
||||
// Forward declarations
|
||||
class AcceptedLedger;
|
||||
@@ -180,7 +180,7 @@ public:
|
||||
virtual InboundTransactions&
|
||||
getInboundTransactions() = 0;
|
||||
|
||||
virtual TaggedCache<uint256, AcceptedLedger>&
|
||||
virtual TaggedCache<UInt256, AcceptedLedger>&
|
||||
getAcceptedLedgerCache() = 0;
|
||||
|
||||
virtual LedgerMaster&
|
||||
@@ -237,7 +237,7 @@ public:
|
||||
virtual Logs&
|
||||
getLogs() = 0;
|
||||
|
||||
[[nodiscard]] virtual std::optional<uint256> const&
|
||||
[[nodiscard]] virtual std::optional<UInt256> const&
|
||||
getTrapTxID() const = 0;
|
||||
|
||||
/**
|
||||
|
||||
@@ -207,7 +207,7 @@ private:
|
||||
std::condition_variable cv_; // signaled when all threads paused
|
||||
std::mutex mut_;
|
||||
bool allPaused_{true};
|
||||
semaphore semaphore_; // each pending task is 1 resource
|
||||
Semaphore semaphore_; // each pending task is 1 resource
|
||||
int numberOfThreads_{0}; // how many we want active now
|
||||
std::atomic<int> activeCount_; // to know when all are paused
|
||||
std::atomic<int> pauseCount_; // how many threads need to pause now
|
||||
|
||||
@@ -91,6 +91,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using semaphore = BasicSemaphore<std::mutex, std::condition_variable>;
|
||||
using Semaphore = BasicSemaphore<std::mutex, std::condition_variable>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -526,7 +526,7 @@ class ValueIteratorBase
|
||||
{
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using size_t = unsigned int;
|
||||
using SizeT = unsigned int;
|
||||
using difference_type = int;
|
||||
using SelfType = ValueIteratorBase;
|
||||
|
||||
@@ -593,7 +593,7 @@ class ValueConstIterator : public ValueIteratorBase
|
||||
friend class Value;
|
||||
|
||||
public:
|
||||
using size_t = unsigned int;
|
||||
using SizeT = unsigned int;
|
||||
using difference_type = int;
|
||||
using value_type = Value const;
|
||||
using reference = Value const&;
|
||||
@@ -658,7 +658,7 @@ class ValueIterator : public ValueIteratorBase
|
||||
friend class Value;
|
||||
|
||||
public:
|
||||
using size_t = unsigned int;
|
||||
using SizeT = unsigned int;
|
||||
using difference_type = int;
|
||||
using value_type = Value;
|
||||
using reference = Value&;
|
||||
|
||||
@@ -48,33 +48,33 @@ public:
|
||||
struct FeatureInfo
|
||||
{
|
||||
FeatureInfo() = delete;
|
||||
FeatureInfo(std::string n, uint256 const& f, VoteBehavior v)
|
||||
FeatureInfo(std::string n, UInt256 const& f, VoteBehavior v)
|
||||
: name(std::move(n)), feature(f), vote(v)
|
||||
{
|
||||
}
|
||||
|
||||
std::string const name;
|
||||
uint256 const feature;
|
||||
UInt256 const feature;
|
||||
VoteBehavior const vote;
|
||||
};
|
||||
|
||||
virtual ~AmendmentTable() = default;
|
||||
|
||||
[[nodiscard]] virtual uint256
|
||||
[[nodiscard]] virtual UInt256
|
||||
find(std::string const& name) const = 0;
|
||||
|
||||
virtual bool
|
||||
veto(uint256 const& amendment) = 0;
|
||||
veto(UInt256 const& amendment) = 0;
|
||||
virtual bool
|
||||
unVeto(uint256 const& amendment) = 0;
|
||||
unVeto(UInt256 const& amendment) = 0;
|
||||
|
||||
virtual bool
|
||||
enable(uint256 const& amendment) = 0;
|
||||
enable(UInt256 const& amendment) = 0;
|
||||
|
||||
[[nodiscard]] virtual bool
|
||||
isEnabled(uint256 const& amendment) const = 0;
|
||||
isEnabled(UInt256 const& amendment) const = 0;
|
||||
[[nodiscard]] virtual bool
|
||||
isSupported(uint256 const& amendment) const = 0;
|
||||
isSupported(UInt256 const& amendment) const = 0;
|
||||
|
||||
/**
|
||||
* @brief returns true if one or more amendments on the network
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
* Returns a json::ValueType::Object.
|
||||
*/
|
||||
[[nodiscard]] virtual json::Value
|
||||
getJson(uint256 const& amendment, bool isAdmin) const = 0;
|
||||
getJson(UInt256 const& amendment, bool isAdmin) const = 0;
|
||||
|
||||
/**
|
||||
* Called when a new fully-validated ledger is accepted.
|
||||
@@ -122,33 +122,33 @@ public:
|
||||
virtual void
|
||||
doValidatedLedger(
|
||||
LedgerIndex ledgerSeq,
|
||||
std::set<uint256> const& enabled,
|
||||
majorityAmendments_t const& majority) = 0;
|
||||
std::set<UInt256> const& enabled,
|
||||
MajorityAmendmentsT const& majority) = 0;
|
||||
|
||||
// Called when the set of trusted validators changes.
|
||||
virtual void
|
||||
trustChanged(hash_set<PublicKey> const& allTrusted) = 0;
|
||||
trustChanged(HashSet<PublicKey> const& allTrusted) = 0;
|
||||
|
||||
// Called by the consensus code when we need to
|
||||
// inject pseudo-transactions
|
||||
virtual std::map<uint256, std::uint32_t>
|
||||
virtual std::map<UInt256, std::uint32_t>
|
||||
doVoting(
|
||||
Rules const& rules,
|
||||
NetClock::time_point closeTime,
|
||||
std::set<uint256> const& enabledAmendments,
|
||||
majorityAmendments_t const& majorityAmendments,
|
||||
std::set<UInt256> const& enabledAmendments,
|
||||
MajorityAmendmentsT const& majorityAmendments,
|
||||
std::vector<std::shared_ptr<STValidation>> const& valSet) = 0;
|
||||
|
||||
// Called by the consensus code when we need to
|
||||
// add feature entries to a validation
|
||||
[[nodiscard]] virtual std::vector<uint256>
|
||||
doValidation(std::set<uint256> const& enabled) const = 0;
|
||||
[[nodiscard]] virtual std::vector<UInt256>
|
||||
doValidation(std::set<UInt256> const& enabled) const = 0;
|
||||
|
||||
// The set of amendments to enable in the genesis ledger
|
||||
// This will return all known, non-vetoed amendments.
|
||||
// If we ever have two amendments that should not both be
|
||||
// enabled at the same time, we should ensure one is vetoed.
|
||||
[[nodiscard]] virtual std::vector<uint256>
|
||||
[[nodiscard]] virtual std::vector<UInt256>
|
||||
getDesired() const = 0;
|
||||
|
||||
// The function below adapts the API callers expect to the
|
||||
|
||||
@@ -43,7 +43,14 @@ enum ApplyFlags : std::uint32_t {
|
||||
|
||||
// Transaction shouldn't be applied
|
||||
// Signatures shouldn't be checked
|
||||
TapDryRun = 0x1000
|
||||
TapDryRun = 0x1000,
|
||||
|
||||
// Transaction is being preflighted as the payload of a
|
||||
// TransactionProposalCreate. Its signatures are collected on-ledger
|
||||
// afterward, so signature-presence checks (e.g. Batch signer matching)
|
||||
// are skipped at proposal-creation time (On-Chain Cosigner spec
|
||||
// §5.3.1.2).
|
||||
TapProposal = 0x2000
|
||||
};
|
||||
|
||||
constexpr ApplyFlags
|
||||
@@ -140,8 +147,8 @@ private:
|
||||
dirAdd(
|
||||
bool preserveOrder,
|
||||
Keylet const& directory,
|
||||
uint256 const& key,
|
||||
std::function<void(SLE::ref)> const& describe);
|
||||
UInt256 const& key,
|
||||
std::function<void(SLE::Ref)> const& describe);
|
||||
|
||||
public:
|
||||
ApplyView() = default;
|
||||
@@ -189,7 +196,7 @@ public:
|
||||
* The key is no longer associated with the SLE.
|
||||
*/
|
||||
virtual void
|
||||
erase(SLE::ref sle) = 0;
|
||||
erase(SLE::Ref sle) = 0;
|
||||
|
||||
/**
|
||||
* Insert a new state SLE
|
||||
@@ -211,7 +218,7 @@ public:
|
||||
* @note The key is taken from the SLE
|
||||
*/
|
||||
virtual void
|
||||
insert(SLE::ref sle) = 0;
|
||||
insert(SLE::Ref sle) = 0;
|
||||
|
||||
/**
|
||||
* Indicate changes to a peeked SLE
|
||||
@@ -231,7 +238,7 @@ public:
|
||||
*/
|
||||
/** @{ */
|
||||
virtual void
|
||||
update(SLE::ref sle) = 0;
|
||||
update(SLE::Ref sle) = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -326,7 +333,7 @@ public:
|
||||
dirAppend(
|
||||
Keylet const& directory,
|
||||
Keylet const& key,
|
||||
std::function<void(SLE::ref)> const& describe)
|
||||
std::function<void(SLE::Ref)> const& describe)
|
||||
{
|
||||
if (key.type != ltOFFER)
|
||||
{
|
||||
@@ -365,8 +372,8 @@ public:
|
||||
std::optional<std::uint64_t>
|
||||
dirInsert(
|
||||
Keylet const& directory,
|
||||
uint256 const& key,
|
||||
std::function<void(SLE::ref)> const& describe)
|
||||
UInt256 const& key,
|
||||
std::function<void(SLE::Ref)> const& describe)
|
||||
{
|
||||
return dirAdd(false, directory, key, describe);
|
||||
}
|
||||
@@ -375,7 +382,7 @@ public:
|
||||
dirInsert(
|
||||
Keylet const& directory,
|
||||
Keylet const& key,
|
||||
std::function<void(SLE::ref)> const& describe)
|
||||
std::function<void(SLE::Ref)> const& describe)
|
||||
{
|
||||
return dirAdd(false, directory, key.key, describe);
|
||||
}
|
||||
@@ -399,7 +406,7 @@ public:
|
||||
*/
|
||||
/** @{ */
|
||||
bool
|
||||
dirRemove(Keylet const& directory, std::uint64_t page, uint256 const& key, bool keepRoot);
|
||||
dirRemove(Keylet const& directory, std::uint64_t page, UInt256 const& key, bool keepRoot);
|
||||
|
||||
bool
|
||||
dirRemove(Keylet const& directory, std::uint64_t page, Keylet const& key, bool keepRoot)
|
||||
@@ -412,7 +419,7 @@ public:
|
||||
* Remove the specified directory, invoking the callback for every node.
|
||||
*/
|
||||
bool
|
||||
dirDelete(Keylet const& directory, std::function<void(uint256 const&)> const&);
|
||||
dirDelete(Keylet const& directory, std::function<void(UInt256 const&)> const&);
|
||||
|
||||
/**
|
||||
* Remove the specified directory, if it is empty.
|
||||
@@ -459,20 +466,20 @@ std::uint64_t
|
||||
createRoot(
|
||||
ApplyView& view,
|
||||
Keylet const& directory,
|
||||
uint256 const& key,
|
||||
std::function<void(SLE::ref)> const& describe);
|
||||
UInt256 const& key,
|
||||
std::function<void(SLE::Ref)> const& describe);
|
||||
|
||||
auto
|
||||
findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start);
|
||||
findPreviousPage(ApplyView& view, Keylet const& directory, SLE::Ref start);
|
||||
|
||||
std::uint64_t
|
||||
insertKey(
|
||||
ApplyView& view,
|
||||
SLE::ref node,
|
||||
SLE::Ref node,
|
||||
std::uint64_t page,
|
||||
bool preserveOrder,
|
||||
STVector256& indexes,
|
||||
uint256 const& key);
|
||||
UInt256 const& key);
|
||||
|
||||
std::optional<std::uint64_t>
|
||||
insertPage(
|
||||
@@ -480,10 +487,10 @@ insertPage(
|
||||
std::uint64_t page,
|
||||
SLE::pointer node,
|
||||
std::uint64_t nextPage,
|
||||
SLE::ref next,
|
||||
uint256 const& key,
|
||||
SLE::Ref next,
|
||||
UInt256 const& key,
|
||||
Keylet const& directory,
|
||||
std::function<void(SLE::ref)> const& describe);
|
||||
std::function<void(SLE::Ref)> const& describe);
|
||||
|
||||
} // namespace directory
|
||||
} // namespace xrpl
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
OpenView& to,
|
||||
STTx const& tx,
|
||||
TER ter,
|
||||
std::optional<uint256> parentBatchId,
|
||||
std::optional<UInt256> parentBatchId,
|
||||
bool isDryRun,
|
||||
beast::Journal j);
|
||||
|
||||
@@ -81,10 +81,10 @@ public:
|
||||
visit(
|
||||
OpenView& target,
|
||||
std::function<void(
|
||||
uint256 const& key,
|
||||
UInt256 const& key,
|
||||
bool isDelete,
|
||||
SLE::const_ref before,
|
||||
SLE::const_ref after)> const& func);
|
||||
SLE::ConstRef before,
|
||||
SLE::ConstRef after)> const& func);
|
||||
|
||||
private:
|
||||
std::optional<STAmount> deliver_;
|
||||
|
||||
Loaded 100 of 1019 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user